2015年5月26日 星期二

拍攝產品照片


公司近期另一條產品線的樣本已經製作完成,需要到影樓拍攝一輯造型照。我們還找來一男一女模特兒拿著產品拍照。今次是第二次需要到影樓監督拍攝工作。第一次是六年前找 G.E.M. 為《極速快車手》拍攝的廣告造型照。而在 Gameone 時找張柏芝拍的《求婚365日2》則由其他同事負責,我沒有參與。

2015年5月21日 星期四

第七次探望 Sita


今天要再到科學園公司,主要是物色能對數據進行分析及處理的應徵者見面。昨天沒有時間探望 Sita,今天就一早過去。第七次探望 Sita。天色雖然不好,但沒有下雨。鮮花總是在靈前出現。

2015年5月20日 星期三

巧遇 Crystal

今天一早有個約會,跟浸會大學的數學教授商討公司旗下應用程式所用到的算法。地點是 Pacific Coffee。大意的我還以為是科學園內的 Pacific Coffee。確保不會遲到的我,於是一早跑到科學園。豈料在約會二十五分鐘前才發現地點是九龍塘又一城的 Pacific Coffee (T_T)。唯有匆忙地趕過去。原本打算面見後可以去探 Sita 的願望落空了。

第一次見面我就遲到了五分鐘,幸好教授沒有介意。個半小時的面見結束後,本來打算回到牛頭角公司;但一場來到,還是到 PageOne 看看有沒有新書。發現《Creativity Inc》中文版推出了,思量過後還是先看家中的英文版而沒有購買,準備打道回公司。就在又一城跟地鐵連接的電梯,我遇到了 Sita 妹妹 Crystal。不知為何,有點開心的感覺。Crystal 還特意走上前,實在很有禮貌。我跟 Crystal 見過三次面,想她認得出我卻不知我的名字。這個不重要。大家順路,在那一刻,得找找話題。畢竟大家相識卻不認識。找話題確是一個難題。其實我有很多關於 Sita 的問題想問 Crystal,但我更想尊重眼前的她。幸好,還是能聊聊工作的事,不至於沒有交談地走到地鐵。今天拜訪不到家姐,卻能遇上妹妹。這個巧合為繁忙的一天帶來一點輕鬆。

2015年5月19日 星期二

Javascript: 憑 User Agent 判斷 iOS / Android

替客戶編寫的流動應用程式能掃瞄二維碼。這個二維碼在我們的程式中才能正常運作;如利用第二方掃瞄工具時,會跳到稱為「Landing Page」的地方。然而,我建議客戶一個細心的設計。若用家利用手機打開著陸頁時,便會自動跳到相關的應用商店,並顯示我們的應用程式。客戶同意這樣的做法。以前是用 PHP 偵測後輸出相應的 HTML;今次則嘗試用 Javascript 來達成:
<script type="text/javascript">
    var userAgent = navigator.userAgent.toLowerCase();
    var isiOS = /(ipad|iphone|ipod)/g.test(userAgent);
    var isAndroid = /(android)/g.test(userAgent);

    if (isiOS == true)  {alert("iOS");}
    if (isAndroid == true)  {alert("Android");}
</script>

2015年5月18日 星期一

Inverse Kinematics


機械臂在編程上需要用到 Inverse Kinematics 來把平面的 (x, y) 座標轉換成 4 個 DOF(Degree of Freedom)的角度。我以 Alpha, Beta, Delta, Gamma 代表。四個角度中,以上圖的 Gamma 最容易計算:


至於另外三個自由度,難度增加了。先看看側面圖:

參考了 Oscar Liang 的公式;先要找出 L 的長度,才能計算出 Alpha 及 Beta 值。利用「畢氏定理」計算 L 值:

有了 L 之後,運用「Cosine Rules」得出 Alpha 值:



同樣運用「Cosine Rules」得出 Beta 值:


最後的 Delta 很容易計算,運用初中數學便能找到:


亦即是說:

2015年5月17日 星期日

修改:Mac mini 的解像度

女兒用電腦的機會多了,原本使用的 Fujitsu 筆記本電腦又要跟太太爭,於是我把不多用的 Mac mini 重灌,打算給女兒使用。配合房間內的 32" SONY 平面電視,比起 15" 的屏幕能看得更加舒服。

電視機的解像度為 1280W x 768H,可是 Mac mini 卻只能偵測及使用 1024W x 768H。畫面少了一部份,看的內容變得少許多。反而在 Boot Camp 下卻能使用 1280W x 768H。明明是相同的硬體,實在奇怪。既然能夠正常輸出 1280W x 768H,那麼我相信是 Mac OS X 10.9 的問題,估計能手動修改。


打開 Finder 並按 Ctrl-Shift-G 輸入「~/Library/Perferences/ByHost」及點選「Go」。找出「com.apple.windowserver」字樣的檔案。把它拉到 TextWrangler 修改:

由於目前的解像度是 1024W x 768H 跟電視的解像度 1280W x 768H 只在闊度方面不同,我把所有「1024」變成「1280」後儲存,重啟 Mac mini 後便能支援 1280W x 768H 解像度了!

2015年5月16日 星期六

新機械臂設計・五


《AMIGO Arm》的外殻部份已經完成,接著便是電子及軟件部份。

原本打算用 Futaba S3003 伺服馬達,但 TowerPro 的 MG995 也有一大堆,而且力量比 S3003 大,於是改用 MG995。MG995 伺服馬達在幾年前購入,在還沒測試好的情況下已經安裝到手臂上,要寫個簡單的程式測試它們是否運作正常。其實我都打算用 Arduino Nano 作為主板,索性寫個 Arduino 程式來測試:
//------------------------------------------------------------------------------
//  AMIGO Arm for Arduino Nano
//  Copyright Pacess HO, 2015.  All rights reserved.
//------------------------------------------------------------------------------

#include <Servo.h> 

//------------------------------------------------------------------------------
//  Global variables
Servo _servoAlpha;
Servo _servoBeta;
Servo _servoGamma;
Servo _servoZeta;

//  Pin or port used for servo
int _pinAlpha = 11;
int _pinBeta = 10;
int _pinGamma = 9;
int _pinZeta = 6;

//  Initialise angles
int _angleAlpha = 90;
int _angleBeta = 180;
int _angleGamma = 135;
int _angleZeta = 90;

int _status;

//==============================================================================
void setup()  {
    _status = 0;

    _pinAlpha = 11;
    _pinBeta = 10;
    _pinGamma = 9;
    _pinZeta = 6;

    _angleAlpha = 90;
    _angleBeta = _angleAlpha;
    _angleGamma = _angleAlpha;
    _angleZeta = _angleAlpha;

    Serial.begin(115200);

    _servoAlpha.attach(_pinAlpha);
    _servoBeta.attach(_pinBeta);
    _servoGamma.attach(_pinGamma);
    _servoZeta.attach(_pinZeta);

    _servoAlpha.write(_angleAlpha);
    _servoBeta.write(_angleBeta);
    _servoGamma.write(_angleGamma);
    _servoZeta.write(_angleZeta);
} 

//------------------------------------------------------------------------------
void loop()  {

    int delta = 1;
    while (true)  {

    _angleAlpha += delta;
    _angleBeta += delta;
    _angleGamma += delta;

    if (_angleAlpha >= 140)  {delta = -1;}
    if (_angleAlpha <= 40)  {delta = 1;}
    Serial.print(_angleAlpha);
    Serial.print("\n");

    _servoAlpha.write(_angleAlpha);
    _servoBeta.write(_angleBeta);
    _servoGamma.write(_angleGamma);
    _servoZeta.write(_angleZeta);
    delay(20);
    }
}

不過結果很奇怪。MG995 在程式開動時沒有動作,需要人手外加旋轉才能開始轉動。Arduino Nano 及伺服馬達使用不同電源,兩者都是 5v,理應運作順利。要是換上 S3003 的話卻能正常運作。查閱過 MG995 的 Datasheet 也說明了工作電壓 4.8v - 6.0v。一切看似正常,難度要提升到 6v?需要點工具才能準備得到...。

2015年5月14日 星期四

Objective-C: AES 加密解密

為容戶重製的應用程式中,不止圖檔會被加密,連客戶端及服務器端的溝通都會被加密。那麼,客戶端收到數據後便需要進行解密,同時處理完的資訊又要加密後才發到服務器。因此,需要為客戶端編寫加密解密的程序。參考了Zhiwei Li 的 iPhone上AES加密的实现,稍作修改後便能使用。下面的代碼就是運用了加密解密的部份:
NSDictionary *jsonDictionary = @{
    @"deviceID": deviceID,
    @"appVersion": appVersion,
    @"jailBroken": jailBroken,
    @"timeStamp": timeStamp
};

//--------------------------------------------------------------------------------------------------
//  Encode to JSON data
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary options:0 error:&error];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

//  Convert JSON to AES-256
NSString *aesString = [jsonString aes256EncryptWithKey:APPMANAGER_KEY_AES256];

//  Calculate checksum
NSString *checkSumString = @"checkSum";

//--------------------------------------------------------------------------------------------------
//  Send it out to server
NSString *urlString = [APPMANAGER_API_HOST stringByAppendingString:APPMANAGER_API_REGISTER];
NSDictionary *dictionary = @{
    @"data": aesString,
    @"checkSum": checkSumString
};

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
[manager POST:urlString parameters:dictionary success:^(AFHTTPRequestOperation *operation, id responseObject)  {
    
    //--------------------------------------------------------------------------------------------------
    //  Success, but never mind the result, just for record
    NSDictionary *dictionary = (NSDictionary *)responseObject;
    NSString *dataString = [dictionary objectForKey:@"data"];

    NSString *plainText = [dataString aes256DecryptWithKey:APPMANAGER_KEY_AES256];
    BZLog(@"Connection done! %@", plainText);

}  failure:^(AFHTTPRequestOperation *operation, NSError *error)  {
    
    //--------------------------------------------------------------------------------------------------
    //  Failed, but never mind the result, just for record
    BZLog(@"### %@", [error localizedDescription]);
}];

有一點要留意,就是加密解密要使用相同的算法及長度。我經過反覆測試後,選擇了 AES 256bits 及 ECB 模式。原因是使用 CBC 時發現解碼不能 100% 正確。

2015年5月13日 星期三

新機械臂設計・四


機械臂還欠缺底盤,所以設計一下。為方便轉動,底盤選擇了圓碟設計。考慮到整條手臂的重量會落在圓碟上,於是加入三顆波子當作滑輪之餘,也能承托一點重量。波子是以前喝「波子汽水」後得來的。打印出來後,尺寸很合身。可是波子的頂部跟底盤還有一段距離,跟本無法承托。看來是量度是計錯數了。恰巧粉紅色料也快將用盡,雖有另外設計一層,把它套在上面吧...。

2015年5月12日 星期二

新機械臂設計・三


兩個主關節打印及安裝好後,就輪到拿筆的手及底部的關節。本來底部關節只有一個 L 字形的部件,安裝後發現比較受力,還是雙關節會來得穩陣。於是把 L 部件的設計修改一下,打印兩個出來,效果不錯。之後就是頭痛的底盤設計...。

2015年5月11日 星期一

PHP:把圖檔以 AES-128 ECB 加密

知名客戶今年再找我們開發流動應用程式。距離上架還有一段時間,於是作出新嘗試:把應用內的圖檔加密。

我看過很多的應用都有這樣的處理,特別是雜誌類應用。它們雖然加密得不錯。說是不錯,是因為某些應用就算用 AES-256 加密,但在 Snoop-It 下,加密匙還是輕易被抽出來,根本不用暴力攻擊,保護已經完全瓦解。不過,任何系統都有弱點。保護都是用來防一般大眾,有保護總比沒有的好。簡單編寫了一個 PHP 程式,把圖檔以 AES-128 ECB 進行加密的實驗:
<?php
    $folder = "./files/";
    $filename = "coupon_01.png";
    $outputFile = "coupon_01_aes.png";

    $ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);  
    $iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);  
    $key = "*16-bytes-length";

    //  Read file content
    $pngRAW = file_get_contents($folder.$filename);

    //  Encrypt file content
    $pngCipher = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $pngRAW, MCRYPT_MODE_ECB);  
    file_put_contents($folder.$outputFile, $pngCipher);
?>

翻看 coupon_01.png 及 coupon_01_aes.png,實驗成功把檔案內容改變了。下一歲便是在 iOS 及 Android 上解密。

2015年5月10日 星期日

新機械臂設計・二


修理完 ArrayZ 後,立即拿一個簡單的模型測試。效果滿意。於是把早幾天畫好了的機械臂打印出來。安裝過後發現臂不夠長,同時摺起來還有些位置接觸不到,需要稍作改良。

得到 Chris 的建議,在貼滿縐紋膠紙的平台上塗上一層薄薄的漿糊來提升貼地力。效果不錯。不過,打印完成後需要再作清理。

2015年5月9日 星期六

TP-LINK TL-WN722NC


為 Raspberry Pi 買了兩條 WiFi 手指。第一條是 D-Link DWA-131。能在 Raspberry Pi 上正常運作;第二條是 TP-LINK TL-WN725N。除了能正常運作之外,還能支援熱點模式(Access Point)。可是由於設計太過輕巧,接收能力有些時候不夠穩定。於是今天到深水埗黃金商場買入一條帶天線的 TP-LINK TL-WN722NC。售價 HK$105。它有另一個沒帶底座的版本 TP-LINK TL-WN722N,售價最低 HK$88。選這個型號還有是因為它支持監聽模式(Monitor Mode),有多一點功用。

除此之外,順道多買一張 32GB SD 卡。早一兩個月時買,最便宜也要 HK$96。現在全場很多店舖售 HK$85。電腦產品的降價實在很快...。

2015年5月8日 星期五

ArrayZ:另一個修正平台距離的方法


上周 Chris 已經幫我把 ArrayZ 的打印頭清理妥當。可是回家安裝後發現平台與打印頭的距離無論如何設定,也有約 3-4mm 的高度。對於打印高度 0.3-0.5mm 左右的範圍,實在超出太多。發生這樣的問題,估計是打印頭解拆再安裝後,以目測來看是比原先短了,導致高度增加了。由於探針本體沒有伸縮設計,加上安裝的位置是固定,無法直接作出調整。不過,我倒找到了一個臨時解決方法令探針提起 2mm 左右的距離。我在探針金屬部份與打印頭的亞加力膠固定部件的中間插入了泥睇,貪其容易改變形狀而又有一定的貼力。結果成功把平台設定到適當的高度,可以繼續打印。

2015年5月7日 星期四

為 Mac Terminal 的 Prompt 加入色彩

之前為 Mac Terminal 的 Prompt 加入時間還是不夠,我希望帶點顏色。於是再次修改一下:
23:24:44 MacBook-Pro-Pacess:~ pacess$ nano .bash_profile
[[ -s "$HOME/.profile" ]] && source "$HOME/.profile" # Load the default .profile

[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # Load RVM into a shell session *as a function*

##  30m = Black text
##  31m = Red text
##  32m = Green text
##  33m = Yellow text
##  34m = Blue text
##  35m = Purple text
##  36m = Cyan text
##  37m = White text
export PS1="\[\033[35m\]\t\[\033[m\] \h:\w \[\033[33m\]\u$\[\033[m\] \[\e[0m\]"
export CLICOLOR=1
如果在代碼前加入「1;」如:「1;35m」則會較為明亮。如要改動 Raspberry Pi 的 Prompt,則修改「nano ~/.bashrc」。

2015年5月6日 星期三

在 RPi 定義 WiFi 手指用的 wlan 編號

手上有兩粒 WiFi USB 手指。一粒支持熱點,另一粒不支援。因應不同情況,會使用相應的 WiFi 手指。當單獨使用 WiFi 手指時,它們均被獲指派為 wlan0;若一起使用時,由於支持熱點的 WiFi 手指的 MAC 地址數值較細,會首先被 Raspberry Pi 找到,並指派為 wlan0。而不支援熱點的 WiFi 手指則被指定為 wlan1。

可是問題來了。不支援熱點的 WiFi 手指有時被指派為 wlan0,但有時又被指派為 wlan1;那麼 WiFi 連線設定便只能在 wlan0 或 wlan1 時才能正確。為此,我翻查了很多網頁,最後找到了定義 WiFi 手指 wlan 編號的方法。我打算把不支援熱點的 WiFi 手指設定為 wlan1,把支持熱點的 WiFi 手指定義為 wlan2。wlan0 不用。首先要在 Raspberry Pi 上得出 WiFi 手指的 MAC 地址:
pi@amigo-camera ~ $ ifconfig
wlan0     Link encap:Ethernet  HWaddr 11:22:33:44:55:66
          inet addr:192.168.1.1  Bcast:192.168.1.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:11 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
記錄起 MAC 地址後,在指令欄輸入:
pi@amigo-camera ~ $ sudo nano /etc/udev/rules.d/70-persistent-net.rules
輸入內容:
##  Assign wlan# according to specify MAC address
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="11:22:33:44:55:66", ATTR{type}=="1", KERNEL=="wlan*", NAME="wlan1"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="aa:bb:cc:dd:ee:ff", ATTR{type}=="1", KERNEL=="wlan*", NAME="wlan2"
重啟一次便大功告成!

參考網址:http://unix.stackexchange.com/questions/73595/wlan-number-assignment

2015年5月5日 星期二

新機械臂設計


得到香港 3D 打印教父 Chris 熱情的幫助,原本塞了的打印頭再次回到工作崗位。可是引伸出另一個問題。平台跟打印頭之間的高度無論怎樣用探針調校都沒有改善,唯有繼續尋找解決方法。

與此同時,一直都想設計一隻機械臂幫我處理沉悶的工作。之前設計的版本,太闊臂不夠長,還是想要簡單一點的版本。於是畫了一個新的設計。希望在修正了打印頭高度後能嘗試打印,測試伺服馬達是否夠力。

2015年5月4日 星期一

在 Mac 上燒錄 Windows 10 Insider Preview 到 USB 手指


為了嘗試《Windows 10 IoT》,我決定把《Windows 10 Insider Preview》燒錄到 USB 手指,並在 MacBook Pro 上面安裝。試過兩次用《Disk Utility》燒錄出來的手指都會在系統啟動時無法執行,只好改用其他方法。以前試過用《BootCamp》安裝《Windows 7》及《Windows 8》,於是試試這個方法。最後成功了。之後也成功把《Windows 10 IoT》燒錄到 USB 手指。可是目前的《Windows 10 IoT》完全沒有功能,十分失望。

2015年5月3日 星期日

在 Mac 上錄影 iPhone / iPad 畫面


女兒的同學不懂得玩《プリティーリズム·シェイク - プリシェイ》,打算錄影示範給她們看。以前我用的軟件是《X-Mirage》或《Screenflick》。將 iPhone 畫面 AirPlay 到 Mac 機後進行錄影。但升級到《Yosemite》後部份工具無法使用,或由於授權號碼遺失了,導致錄影無法進行。只好找找其他方法。


原來要在《Yosemite》錄影是非常容易。只要 iPhone 升級到 iOS 8.0 或以後,利用 Lightening 線接駁到 Mac 機,以最新版《QuickTime》便能錄影。打開《QuickTime》後在頂方的下拉選單選「New Movie Recording」。預設錄影源是內解鏡頭,只要點擊在錄影鍵旁的向下箭咀,便能把輸入源改為 iPhone。留意畫面源及聲音源是分開設定。

2015年5月2日 星期六

《星光少女》iOS 版本運作


大女愛上《星光少女》,知道出了 iOS 版遊戲,當然第一時間下載。爸爸當然變成她的練功工具。除了為她的角色儲經驗值、一首一首歌解鎖之外,我也順道了解一下它的運作。

跟很多的遊戲一樣,《星光少女》不連上網是無法進行。我好奇連線時有甚麼的東西在處理,於是拿《Charles》記錄當中的通訊。發現開始時會通知服務器進行 login 步驟;在開始遊戲前會通知服務器進行 ingamefriend 步驟;在開始遊戲時是 startgame;在遊戲完結時是 endgame。這樣做能有效監管遊戲的運作,減少玩家出千的機會。還有其他個別情況的 getgifts、receivegifts、petequip、cardevolve...等。它們的內容都是相同的結構。一個資料及一個驗證碼:
{"data":"eyJzdGFydGdhbWUiOnsic3RhdHVzIjoxLCJnYW1ldWlkIjoiNTU
0MzkzNmFmODAxNDI0ZTdkOGI0Yzk3Iiwic3luYyI6MC4wMiwic3BlY2lhbG5
vdGUiOnsiZHJvcFJhdGUiOjQsIm1heCI6NSwiZXZlbnRpZCI6IkVWRU5UXzI
    ::    ::    ::    ::    ::    ::    ::    ::    ::
uZXRcL1BUUlwvYXNzZXRcL3VwZGF0ZVwvIn0sIm5ld0NhcmRzIjpbIjhfNl8
yIiwiOV82XzIiLCIxMF82XzIiLCI4XzdfMSIsIjlfN18xIiwiMTBfN18xIl0
sIm5vdGljZSI6bnVsbCwiYXR0ZW5kIjpudWxsfX0=","chkSum":"03e4f99
f1802300824f0b6ed97465685"}
很多時這類資料都以 Base64 來加密,所以我嘗試利用 https://www.base64decode.org/ 來解密:
{
    "startgame":{
        "status":1,
        "gameuid":"5543936af801424e7d8b4c97",
        "sync":0.02,
        "specialnote":{
            "dropRate":4,
            "max":5,
            "eventid":"EVENT_20150425",
            "boost":1
        },
        "status_desc":null
    },
    "init":{
        "profile":{
            "comment":"\u3088\u308d\u3057\u304f\u306d\u266a",
            "nick":"Sita",
            "rank":11,
            "card":{
                "level":4,
                "card_id":"1_2_1",
                "pet_id":null,
                "playCnt":0,
                "att_score":{
                    "1ST":{
                        "LOVELY":2610
                    },
                    "2ND":{
                        "ETHNIC":1507
                    },
                    "3RD":{
                        "COOL":1200
                    }
                },
                "transcendance":0
            },
            "lastPlayTS":1430488784,
            "lastPlayDate":"2015-05-01 22:59:44",
            "collections":{
                "1":1
            },
            "playCnt":17
        },
        "account":{
            "email":"sita@chan.com",
            "sso":true
        },
        "stamina":{
            "max":35,
            "current":29,
            "nextUpdateDate":1430492370,
            "lastUpdateDate":1430492010,
            "maxDate":1430494170
        },
        "deck":{
            "cardDeck":{
                "current":6,
                "max":40
            },
            "petDeck":{
                "current":11,
                "max":20
            },
            "friendDeck":{
                "current":0,
                "max":13
            }
        },
        "nickTag":1006,
        "booster":{
            "booster":null,
            "expiredTS":null
        },
        "stage":{
            "cleared":[
                "PTR001",
                "PTR002",
                "PTR004",
                "PTR003",
                "PTR007",
                "PTR005",
                "PTR008",
                "PTR006",
                "PTR009",
                "PTR010",
                "PTR011",
                "PTR012"
            ],
            "paid":[
                "PTR151",
                "PTR152",
                "PTR153",
                "PTR130",
                "PTR131",
                "PTR132"
            ]
        },
        "stat":{
            "level":11,
            "exp":1995,
            "tutorial":-1,
            "life":12
        },
        "specialNote":{
            "eventItem":71,
            "eventPoint":0,
            "nextRewardPoint":10
        },
        "main_unit":"1ST",
        "unit":{
            "1ST":{
                "name":"\u30e6\u30cb\u30c3\u30c81",
                "card_uid_list":{
                    "1ST":"55436498f9014219598b4bb0",
                    "2ND":null,
                    "3RD":null
                }
            },
            "2ND":{
                "name":"\u30e6\u30cb\u30c3\u30c82",
                "card_uid_list":{
                    "1ST":null,
                    "2ND":null,
                    "3RD":null
                }
            },
            "3RD":{
                "name":"\u30e6\u30cb\u30c3\u30c83",
                "card_uid_list":{
                    "1ST":null,
                    "2ND":null,
                    "3RD":null
                }
            }
        },
        "purchased_music":[
            "PAID_SONG_10",
            "PAID_SONG_3"
        ],
        "addedDeck":{
            "cardDeck":0,
            "petDeck":0,
            "friendDeck":0
        },
        "goods":{
            "friend_point":705,
            "booster":{
                "Stone_Boost_1":0,
                "Stone_Boost_2":0,
                "Coin_Boost_1":0,
                "Coin_Boost_2":0,
                "CardExp_Boost_1":2,
                "CardExp_Boost_2":0,
                "UserExp_Boost_1":1,
                "UserExp_Boost_2":0,
                "Allinone_1":0,
                "Allinone_2":0
            },
            "coin":16609,
            "gem":5,
            "bonus":0,
            "stone":{
                "LOVELY":{
                    "SMALL":17,
                    "MEDIUM":2,
                    "LARGE":0
                },
                "POP":{
                    "SMALL":30,
                    "MEDIUM":0,
                    "LARGE":0
                },
                "COOL":{
                    "SMALL":6,
                    "MEDIUM":0,
                    "LARGE":0
                },
                "FEMININ":{
                    "SMALL":16,
                    "MEDIUM":0,
                    "LARGE":0
                },
                "ETHNIC":{
                    "SMALL":16,
                    "MEDIUM":0,
                    "LARGE":0
                },
                "SEXY":{
                    "SMALL":16,
                    "MEDIUM":0,
                    "LARGE":0
                },
                "STAR":{
                    "SMALL":0,
                    "MEDIUM":0,
                    "LARGE":0
                }
            },
            "ticket":{
                "normal":0,
                "pr":0,
                "sr":0
            }
        },
        "cnt":{
            "feedback":0,
            "gift":0,
            "message":0,
            "friend":{
                "cnt":0,
                "req":1
            }
        },
        "serverTS":1430492010,
        "resourceVer":{
            "resource":{
                "type":"resource",
                "ios":{
                    "card":[
                        {
                            "url":"1426094173\/card\/ios\/card_aira.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/card\/ios\/card_ann.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/card\/ios\/card_bell.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/card\/ios\/card_ito.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/card\/ios\/card_mion.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/card\/ios\/card_naru.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1429558702\/card\/ios\/card_others.assetBundle",
                            "ver":4,
                            "t":false
                        },
                        {
                            "url":"1426094173\/card\/ios\/card_otoha.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/card\/ios\/card_rinne.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/card\/ios\/card_rizumu.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/card\/ios\/card_wakana.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426690286\/card\/ios\/card_update_1.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426758995\/card\/ios\/card_update_2.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1427901046\/card\/ios\/update_0401.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1429558702\/card\/ios\/card_others_2.assetBundle",
                            "ver":2,
                            "t":false
                        },
                        {
                            "url":"1428567871\/card\/ios\/update_0407.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1429170877\/card\/ios\/update_0415.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1429558702\/card\/ios\/update_0420.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1429558702\/card\/ios\/card_others_3.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1430380704\/card\/ios\/update_0429.assetBundle",
                            "ver":1,
                            "t":false
                        }
                    ],
                    "cutscene":[
                        {
                            "url":"1428583242\/cutscene\/ios\/cutscene.assetBundle",
                            "ver":3,
                            "t":true
                        }
                    ],
                    "gamemode":[
                        {
                            "url":"1429869929\/gamemode\/ios\/gamemode.assetBundle",
                            "ver":8,
                            "t":true
                        },
                        {
                            "url":"1426910745\/gamemode\/ios\/particle.assetBundle",
                            "ver":4,
                            "t":true
                        }
                    ],
                    "pet":[
                        {
                            "url":"1426094173\/pet\/ios\/pets.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/pet\/ios\/pets_aurora.assetBundle",
                            "ver":1,
                            "t":false
                        }
                    ],
                    "song":[
                        {
                            "url":"1426094173\/song\/ios\/track_0.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/song\/ios\/track_120.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/song\/ios\/track_150.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/song\/ios\/track_30.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/song\/ios\/track_60.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/song\/ios\/track_90.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1426094173\/song\/ios\/track_others.assetBundle",
                            "ver":1,
                            "t":false
                        },
                        {
                            "url":"1427460413\/song\/ios\/update_0327.assetBundle",
                            "ver":1,
                            "t":false
                        }
                    ],
                    "uniteff":[
                        {
                            "url":"1426094173\/uniteff\/ios\/unit_1.assetBundle",
                            "ver":1,
                            "t":true
                        },
                        {
                            "url":"1426094173\/uniteff\/ios\/unit_2.assetBundle",
                            "ver":1,
                            "t":true
                        },
                        {
                            "url":"1426094173\/uniteff\/ios\/unit_3.assetBundle",
                            "ver":1,
                            "t":true
                        },
                        {
                            "url":"1426094173\/uniteff\/ios\/unit_4.assetBundle",
                            "ver":1,
                            "t":true
                        },
                        {
                            "url":"1426094173\/uniteff\/ios\/unit_5.assetBundle",
                            "ver":1,
                            "t":true
                        },
                        {
                            "url":"1426909951\/uniteff\/ios\/unit_6.assetBundle",
                            "ver":2,
                            "t":true
                        },
                        {
                            "url":"1427901046\/uniteff\/ios\/unit_7.assetBundle",
                            "ver":1,
                            "t":true
                        },
                        {
                            "url":"1427985659\/uniteff\/ios\/card_unit_1.assetBundle",
                            "ver":1,
                            "t":true
                        }
                    ]
                }
            },
            "app_url":{
                "os":{
                    "android":"https:\/\/play.google.com\/store\/apps\/details?id=com.dooub.ptrshake",
                    "ios":"https:\/\/itunes.apple.com\/jp\/app\/id956716079"
                },
                "type":"app_url"
            },
            "version":{
                "os":{
                    "android":"1.2.0",
                    "ios":"1.1.16"
                },
                "type":"version"
            },
            "resource_version":{
                "type":"resource_version",
                "ios":{
                    "song":415,
                    "pet":52,
                    "card":45
                },
                "android":{
                    "song":414,
                    "pet":52,
                    "card":45
                }
            },
            "event":null,
            "coupon_off":{
                "type":"coupon_off",
                "ver":"1.1.20"
            },
            "notice_version":{
                "type":"notice_version",
                "ios":42,
                "android":41,
                "andorid":4
            },
            "special_note":{
                "eventid":"EVENT_20150425",
                "startTS":1429948800,
                "endTS":1431183599,
                "banner":"banner\/pl_event_banner_b20150424_final.png",
                "banner_link":"http:\/\/admin.ptrshake.gooub.com\/web\/eventInfo.do",
                "banner_s":"banner\/pl_event_banner_s20150424_final.png",
                "schedule":null,
                "note_img":"banner\/img_sn_icon_style.png",
                "eventpoint_img":"banner\/img_sn_text_style.png",
                "viewTS":1431356399
            },
            "cdn":"http:\/\/dzrdvwk3byl4f.cloudfront.net\/PTR\/",
            "asset_cdn":"http:\/\/dzrdvwk3byl4f.cloudfront.net\/PTR\/asset\/update\/"
        },
        "newCards":[
            "8_6_2",
            "9_6_2",
            "10_6_2",
            "8_7_1",
            "9_7_1",
            "10_7_1"
        ],
        "notice":null,
        "attend":null
    }
}
成功解開非常詳細的內容。至於後方的驗證碼,是用來確保前方資料沒有被改動過。分別試過將前方數值加起來,及把後方數值以 Base64 解碼,都無法找到驗證碼的運算方式。要是能解開的話,相信能利用 MITMProxy 及 Python 進行即時內容修改。

2015年5月1日 星期五

為 Mac Terminal 的 Prompt 加入時間

昨晚下載了 Windows 10 Insider Preview 的 .iso 影像檔,打算嘗試安裝到 MacBook Air 內。在 Terminal 中輸入
MacBook-Pro-Pacess:~ pacess$ sudo dd if=Windows10_InsiderPreview_x64_EN-GB_10074.iso of=/dev/disk2 bs=1m
3603+1 records in
3603+1 records out
3778686976 bytes transferred in 2279.967642 secs (1657342 bytes/sec)
等了很久仍然未完成燒錄 3.78GB 的工作,畫面又沒有顯示工作是否在進行中。原來總共要花 38 分鐘才能完成。心想如果指令欄能顯示下指令的時間時,能讓我更容易估計程式是否已經沒有反應。這樣的功能在 DOS 年代已經有,當今不可能沒有。在網上調查一下,找到了方法:
MacBook-Pro-Pacess:~ pacess$ nano .bash_profile
在檔案最下方加入「export PS1="\t \h:\w \u$ "」:
[[ -s "$HOME/.profile" ]] && source "$HOME/.profile" # Load the default .profile

[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # Load RVM into a shell session *as a function*

export PS1="\t \h:\w \u$ "
當中的代數為:
  • \d = 日期
  • \t = 時間
  • \h = 電腦名稱
  • \# = 指令編號
  • \u = 用戶名稱
  • \W = 當前目錄
  • \w = 當前完整目錄

    按「Control-X」儲存後,重新打開 Terminal 便會發現 Prompt 成功更新:
    11:54:06 MacBook-Pro-Pacess:~ pacess$ 

    P.S.: 最終以 dd 燒錄出來的 USB 在 Windows 下會無法讀取