2014年4月30日 星期三

Javascript Canvas 示範程式


新同事述職一個月,起初的工作是開發簡單的 Facebook 網頁;現在進一步開發遊戲。為了印證想法是否可行,我希望可以用 Javascript 製作出來,看看玩法有沒有問題。我也順便試試 Javascript Canvas 的開發。
<script type="text/javascript">
   var _particleWidth, _particleHeight;
   var _particleArray = [];
   var _clickX, _clickY;
   var _particleImage;
   var _context;
   var _canvas;
   var _timer;

   function init()  {
      _canvas = document.getElementById("canvas");
      _canvas.addEventListener("mousedown", function(e)  {onMouseDown(e);}, false);
      _canvas.addEventListener("touchstart", function(e)  {onMouseDown(e);}, false);

      _context = _canvas.getContext("2d");

      _particleWidth = 32;
      _particleHeight = 32;
      _particleImage = new Image();
      _particleImage.onload = function()  {
      _particleWidth = _particleImage.width;
      _particleHeight = _particleImage.height;
   }
   _particleImage.src = "http://www.pacess.com/blog_files/icon.png";

   for (var i=0; i<10; i++)  {
      var particleObject = new Particle(300, 300);
      _particleArray[i] = particleObject;
   }

   loopFrame();
   }

   function onMouseDown(e)  {
      var canvasRect = _canvas.getBoundingClientRect();
      _clickX = (e.clientX-canvasRect.left)*(canvasRect.width/canvasRect.width);
      _clickY = (e.clientY-canvasRect.top)*(canvasRect.height/canvasRect.height);

      for (var i=0; i<10; i++)  {
         var particleObject = new Particle(_clickX, _clickY);
         _particleArray.push(particleObject);
      }
   }

   function loopFrame()  {
      for (var i=0; i<_particleArray.length; i++)  {
         var particleObject = _particleArray[i];
         var finished = particleObject.updateFrame();
         if (finished == false)  {continue;}

         _particleArray.splice(i, 1);
         i--;
      }

      clearCanvas();
      for (var i=0; i<_particleArray.length; i++)  {
         var particleObject = _particleArray[i];
         particleObject.drawFrame(_context);
      }

      var count = _particleArray.length;
      _context.fillStyle = "white";
      _context.font = "16px Arial";
      _context.fillText("Particle count: "+count, 10, 20);
      _context.fillText("Please Click on Anywhere", 205, 590);

      window.requestAnimationFrame(loopFrame);
   }

   function clearCanvas()  {
      var width = _canvas.width;
      var height = _canvas.height;

      _context.fillStyle = "#000000";
      _context.fillRect(0, 0, width, height);
   }

   function Particle(x, y)  {
      this.x = x;
      this.y = y;

      this.dx = Math.floor(Math.random()*10)-5;
      this.dy = Math.random()*10;
      this.gravity = 0.2;

      this.angle = Math.random();
      this.rotateSpeed = (Math.floor(Math.random()*5)/20)+0.1;

      //  Type: 0-2
      this.type = Math.floor(Math.random()*3);

      //  Scale
      this.scale = 1/(Math.floor(Math.random()*3));
      this.width = _particleWidth*this.scale;
      this.height = _particleHeight*this.scale;
   }

   Particle.prototype.updateFrame = function()  {
      this.x += this.dx;
      this.dy -= this.gravity;
      this.y -= this.dy;

      this.angle += this.rotateSpeed;

      if (this.x < 0-_particleWidth)  {return true;}
      if (this.x > 600+_particleWidth)  {return true;}
      if (this.y > 600+_particleHeight)  {return true;}
      return false;
   }

   Particle.prototype.drawFrame = function(context)  {
      context.save();
      context.translate(this.x, this.y);
      context.rotate(this.angle);
      context.drawImage(_particleImage, 0, 0, _particleWidth, _particleHeight, -this.width/2, -this.height/2, this.width, this.height);
      context.restore();
   }

   init();
</script>

2014年4月21日 星期一

Sita 陳僖儀雜誌原大圖



花了點時間,從《Mag V》搜集到三本 Sita 陳僖儀的雜誌。分別是:HIM, MRRM, Men's Uno。之前用 iPhone 下載的是 JPG 版本,內容被 AES-128 加密了;但原來用 iPad 下載會是 PDF 版。雖則 PDF 也被加上密碼,但在 /Library/Caches/PDFCaches/xxxxx.pdf/ 內會找到已被解密的 JPG 圖。相信是由 PDF 生成出來作為緩存。那些圖的解像度是 1534x2020,比高清 iPad 細小一點。相信是雜誌的原大版本。我很喜歡這兩張性感的圖片。

2014年4月18日 星期五

跳過越獄檢查


最近女兒跟我都喜歡玩《尖尖》,於是放在《Snoop-it》研究一下。發現幾款 LINE 遊戲內都有 LineGamesSDK 這個類的出現。當中有一個名為 checkJailBreak 的函數;看來是檢查手機是否已越獄。


為了求證,我利用 Cycript 來執行 checkJailBreak,結果傳回 1,亦即是 True。代表已越獄。要跳過檢查,需要修改函數並傳回 0,亦即是 False。這個工作能在 Cycript 達成。輸入「LineGameSDK.messages['checkJailBreak'] = function() {return false;}」把原本的函數取締。修改後再次執行 checkJailBreak 會發現傳回的是 False 了。Cycript 真是一個非常利害的工具!

2014年4月17日 星期四

轉眼已一年


看過幾個不同的術數網,都有關於 Sita 八字的分析。最正確的應該是這個。其中一個術數網竟然能寫出 Sita 的時辰,實在利害。我亦憑幼稚園級數的八字知識嘗試估計一下。Sita 屬戊土,有一妹,八字日柱已有兩土,其他三柱不應有土。得出時辰會是:壬子、乙卯、辛酉、癸亥。而 Sita 過身時的八字是:





十分清楚,Sita 是被「癸水」害死。得出八字喜火土忌金水。所以出生時辰很大機會是「癸亥」。另外,我的看法是:

  • 有報導說 Sita 的前男友嫌她纏身,估計是因為戊土真的很弱,喜比劫相扶,喜歡結交朋友
  • 地支三桃花,陰木生火生身,是好的桃花,故人緣好
  • 既然身弱,「官印相生」格也就理所當然
  • 從 Sita 的性格來看也合理。「官印相生」的人是有自制能力,而且聰明乖巧,愛照顧別人。
  • 要木火的話,那改名為屬丁火的「僖」也合理
  • 加入屬丙火的「太陽娛樂」也很自然,而不是屬金的「英皇娛樂」
  • Sita 喜歡數目字「3」,丙火也
  • 舞台劇「暖 Love Love」的暖屬丁火吧
  • 舞台劇「愛可以多狗」的狗屬戌土,都是喜用的五行
  • 所以 1995-2004 年間比較聰明,讀書入大學不是問題
  • 2005-2014 年桃花乙木透出,踏上成名之路
  • 過身當天下著微雨,那是癸水
  • 駕車是黑色,也是屬水
  • 車是爸爸的,戊土的爸爸也正是水
  • 時辰有三個癸水,身弱擋不住那麼多水的攻擊
  • 不過為何「忘川」那麼受歡迎?川是水吧。因為「忘」川是忘記有水?
  • 至於 Sita 為何喜愛藍色屬水的「多啦 A 夢」,應該是大運甲辰七殺透出。有殺先論殺,而殺比身強,第一運是「食神制殺」格,喜金水吧。也有可能是「多啦 A 夢」是貓屬寅木
  • Sita 不愛吃生果,是因為木剋戊土?身弱還被剋,當然不喜歡啦!

    如果 Sita 能逃過這個劫,往後 40 年都是她的天下。真是可惜。永遠懷念妳!
  • 2014年4月14日 星期一

    實現即時封包修改

    Modify Packet On The Fly


    網友張貼了很多 Sita 的雜誌截圖,於是我也找尋它們的電子版,希望能保留雜誌的 PDF 或原大沒失真的 JPG 檔。終於找到一個 iPad 應用有幾本相關的雜誌。

    為了 Sita 的內容,今次作出了新嘗試。利用接了 LAN 線的 Mac mini,把連線分享給 iPad。這樣,所有 iPad 進出的數據都得經過 Mac mini。較為新鮮的是把當中的內容即時作出修改;例如把 2014 年改為 2015 年。只要在 iPad 設定 Proxy 地址到 Mac mini。在 Mac mini 使用 Charles 建立起 Proxy 服務器,並簡單地編寫一個 Rewrite 指令。要是遇上保安度不足的應用,便能...你明的。

    2014年4月11日 星期五

    第一代 iPad 的「無法下載應用程式」


    客人匯報無法安裝在 OTA 上的 IPA 檔,出現「無法下載應用程式」錯誤。通常是由於 Provisioning 內沒有裝置的 UDID。檢查過不是這個問題。有人指 Apple 在 Xcode 5.1 起不支援舊 iPad 的 OTA 下載,於是找來 Xcode 5.0.1 生成新的 IPA,情況不變。客人使用的是 iPad 2 iOS 7.1,IPA 需要放在 HTTPS,我連 Plist 都放到 HTTP,情況依舊...。


    最後找到問題是基於 Build Settings 中支援 Arm 64。只要除去 Arm 64 相關的兩個設定,生成出來的 IPA 便能在 iPad iOS 5.1.1 上安裝。

    2014年4月9日 星期三

    iBeacon 訊號情況


    iBeacon 推出快一年了,業界行家也推出相應的 iBeacon 應用程式;不過,目前的應用多是:獲得優惠券、記錄人流、區域定位。其實要做出穩定的區域定位已經有點難度,至於大家最希望做到的室內定位,還是沒有人能成功研發。這裡是指精準度在一米以下的定位。今日做了一次實驗,原因不難理解。

    iBeacon 是建基於藍牙之上。藍牙使用的是 2.4GHz 頻譜。大家常用的 WiFi 及無線產品大部份都是 2.4GHz,在在都會影響訊號的質素。圖中是 iBeacon 訊號的變化。實驗在公司單位內進行,有很多的無線設備。得出在靜止及近距離的狀態下,波幅不是太大;但當距離增加後,波幅也隨之上升。對於這樣的結果,在編程上很難做出較準確的定位。而要有待解決。

    2014年4月8日 星期二

    AES-128 JPG 解碼


    這兩天愛上學習 iOS 應用程式的保安知識。越學越感受到 iOS 及 Objective-C 的強大;也越來越覺得「道高一尺,魔高一丈」,難怪 iOS 一直都能被 Jailbreak...。圖中是被加密的 JPG,一直都解不開。但利用這兩天學到的工具:Snoop-it,很容易便得出是 AES-128 加密的結果。再配合編寫 Objective-C 程式,便能順利還回 JPG 的原貌。
    //
    //  main.m
    //  AESDecode
    //
    //  Created by Pacess on 8/4/14.
    //  Copyright (c) 2014 Pacess. All rights reserved.
    //
    
    #include <CommonCrypto/CommonCryptor.h>
    #include <Foundation/Foundation.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    @interface NSData(AES)
    - (NSData *)decryptedDataUsingAESKey:(NSData *)key;
    @end
    
    @implementation NSData(AES)
    
    - (NSData *)decryptedDataUsingAESKey:(NSData *)key  {
       const uint8_t *bytePointer = [self bytes];
       size_t length = [self length];
       if (length < kCCBlockSizeAES128)  {return nil;}
    
       size_t decodeSize = 0;
       CCCryptorStatus result = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                        [key bytes], [key length],
                                        bytePointer,
                                        bytePointer+kCCBlockSizeAES128, length-kCCBlockSizeAES128,
                                        NULL, 0,
                                        &decodeSize);
       if (result != kCCBufferTooSmall)  {return nil;}
    
       void *decodePointer = malloc(decodeSize);
       if (decodePointer == nil)  {return nil;}
    
       result = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                        [key bytes], [key length],
                        bytePointer,
                        bytePointer+kCCBlockSizeAES128, length-kCCBlockSizeAES128,
                        decodePointer, decodeSize,
                        &decodeSize);
       if (result != kCCSuccess)  {free(decodePointer);  return nil;}
       
       NSData *decodedData = [NSData dataWithBytesNoCopy:decodePointer length:decodeSize];
       if (decodedData == nil)  {free(decodePointer);  return nil;}
       return decodedData;
    }
    
    @end
    
    int main(int argc, char * argv[])  {
       @autoreleasepool {
    
          char *aes128Key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
          NSData *key = [NSData dataWithBytesNoCopy:(aes128Key) length:sizeof(aes128Key)-1 freeWhenDone:0];
          int pages[] = {0, 32, 104, 105, 106, 108, -1};
    
          NSString *applicationDirectory = [[NSBundle mainBundle] bundlePath];
          NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
          NSString *documentsDirectory = [paths objectAtIndex:0];
    
          int index = 0;
          while (pages[index] >= 0)  {
    
             int pageID = pages[index++];
             NSString *filename = [NSString stringWithFormat:@"%03d.jpg", pageID];
             NSString *filePath = [applicationDirectory stringByAppendingPathComponent:filename];
             NSData *aes128Data = [NSData dataWithContentsOfFile:filePath];
    
             NSData *jpgData = [aes128Data decryptedDataUsingAESKey:key];
             filename = [@"Decoded_" stringByAppendingString:filename];
             filePath = [documentsDirectory stringByAppendingPathComponent:filename];
             [jpgData writeToFile:filePath atomically:NO];
          }
          return 0;
       }
    }
    

    2014年4月7日 星期一

    公司成立三周年


    今天是公司成立三周年的日子。一如以往,其他合夥人都不記得這個重大的日子。至少對我來說重大。第三年過得很艱苦。希望今年有所改善,不想再過這樣浪費青春的生活。

    2014年4月6日 星期日

    解構《米奇小頑皮》


    看到《米奇小頑皮》的檔案,有點帶我回到 90 年代的感覺。當時的電腦只能在《倚天》或《國喬》下才能顯示出中文字。說到底是進入了 640x480 繪圖模式;而遊戲卻是用 320x240 模式。我們在有限的電腦資源開發遊戲,要把所有中文字都加載到記憶體上是根本沒有可能,唯一的做法是把要用的文字轉成圖案。《米奇小頑皮》正正是使用相同的做法,不過今次不是資源不足,而是要得出指定的描邊風格才這麼做。


    另一個特別的地方是遊戲內沒有用慣常使用 .png 卻改用 .webp。這是由 Google 發明的新圖像格式,擁有像 JPG 般的大小,卻有著 PNG 般的質素及透明,是一個很好的圖檔格式。要打開 .webp 只需要 Chrome 就可以。

    2014年4月4日 星期五

    無法安裝應用程式,因為「www.pacess.com」的憑證無效


    Keith 在 OTA 頁下載舊版的《AMIGO Controller》時出現錯誤並向我求救,於是索性組譯目前最新的版本給他,可是同樣出現『無法安裝應用程式,因為「www.pacess.com」的憑證無效』。本來以為是 UDID 的問題,重新下載 Provisioning Profile 並生成新 .ipa,情況依舊;把 .ipa 由 www.pacess.com 搬到 amigo.pacess.com,問題健在。最後發現是需要 HTTPS,搬到 Dropbox 就行了。我一直用 HTTP 都沒事,不知道何時變成要用 HTTPS...。

    P.S.: 跟據 Howang 提供的資料,是由 iOS 7.1 開始要用 HTTPS

    2014年4月3日 星期四

    我的第一個 Python 程式


    我的手機應用程式,在成立公司時已經加入軟體更新的設計,盡量省卻蘋果審批的次數。亦令心急的客戶能即時把內容推送到用家的機器上,同時確保他們使用的是最新版本。這些工作背後都需要服務器。

    現時我使用的方法都是 HTTP 連接,感覺有些落後。於是嘗試 TCP 方式,看看是否可行以及能用在甚麼場合。今次簡單地編寫了一個 Echo 服務器,就是把輸入來的內容加個當刻時間輸出回去。程式十分簡單,卻是我第一個編寫的 Python 程式。測試是用最簡單不過的 Telnet,證明實驗是成功的。下一步是嘗試由 iPhone 連接,及服務器連接數據庫。

    2014年4月2日 星期三

    解構 Disney Infinity


    在網上找《魔雪奇緣》的玩具時,發現了《Disney Infinity》這套遊戲。看過它的介紹後,已經很想購買一套 PS3 版回來。原來它還出了 iPad 版本。不過,只能用來編輯自己的世界。

    iPad 版《Disney Infinity》中同支援家用機一樣多的角色及物品,所以應用程式的容量超過 1GB。打開 .ipa 後會發現很多 .zip 檔案,這是由於需要支援的內容太多了,把每樣內容的所需資料壓縮成一個個 ZIP 檔是較為方便。資料包括 3D 模型數據、貼圖、動作資料。這些 ZIP 用 Mavericks 預設的 Archive Utility 是打不開,要用 The Unarchiver 才能順利解壓。當中角色的貼圖是以 PVR 格式儲存,大小為 128x128 像素。似乎小了點,但考慮到同一畫面出現太多角色時,就要載入很多貼圖,到時不論記憶體空間或是機器效能也會受到考驗,貼圖還是盡細的好。以下就是《魔雪奇緣》中冰雪女王愛莎及妹妹安娜的貼圖:


    要在 Mavericks 快速預覽 PVR 檔,可以到 http://www.limbic.com/downloads/QuickPVR_v4.zip 下載 Finder 的插件。解壓後把檔案放到 /Library/QuickLook 下,重新登入或在 Terminal 輸入 qlmanage -r 即可。