Friday, March 22, 2013

微單眼要選哪一台?

微單眼選哪一台比較好?

這問題不要說沒有接觸單眼/微單眼的一般人沒有頭緒,連有在玩相機的人可能都很難抉擇。最近非常正好的是身邊同時有幾位朋友都升起了想要買微單眼的念頭而向我詢問,我則根據「個人主觀」給了個人意見。想說也許有人也有這樣的困擾,就一起跟大家分享一下。

目前朋友提到的相機有:
Canon EOS M
Nikon 1 系列
Sony NEX系列
Panasonic GX1
Samsung NX1000

為什麼沒有提到其他廠牌?我也不知道,要怪就怪那些廠牌的行銷預算不夠囉。

其實,三星的NX1000是個不錯的相機,不過缺點也就是因為他是三星,幾乎所有的朋友一律拒買韓國貨,儘管我說NX1000的規格真的不錯也還是沒人願意考慮。

Nikon 1 則是第二個出局的相機。連我這個N家的擁護者都完全不考慮Nikon 1。原因很簡單,因為我所有的N家配備都不能用在Nikon 1上面(除了鏡頭,可以去花個US$200去買個轉接環)。Nikon J1基本上就是個殘廢品,不能外接閃燈,只能用它內建的閃燈。除非你完全用不到閃燈,不然你用它內建的閃燈這樣直光打下去(臉油的人大概油光都出來了),照下去的效果不會比傻瓜好到那裡去。Nikon V1也好不到哪裡去,沒有內建閃光燈,只能買它專用的閃光燈,就算你家有貴貴的Nikon SB-900,一樣不能用在它身上,那麼我幹嘛要買N家?所以光光在閃燈的部分,Nikon 1就完全出局,其他規格根本連值得考慮的空間都沒有。

Sony的習慣就是什麼都跟別人不相容,所以我可以稍微原諒Sony,不過某些NEX系列的機型,比如說NEX-6,除了內建閃燈還有熱靴可以外接閃燈的。加上比別人都大的感光元件(使用APS-C,跟一般單眼一樣大小),還有超強的錄影功能,NEX-6真的是很不錯的選擇。只可惜,它的價格是其他競爭對手的兩倍。

剩下的就是Canon EOS M及Panasonic GX1。

Canon EOS M其實也不錯,我身邊有一位朋友最後就考慮買了這台。不過這台相較於其他兩台有幾個致命的缺點。既然要買微單眼,所考慮的就是其輕巧的機身(相較於單眼)以及其可換鏡頭靈活性,但是Canon EOS M目前沒有輕巧的變焦鏡。除非你一直都使用定焦鏡或是願意等待Canon未來出輕巧型的變焦鏡,不然現階段而言,Canon EOS M變焦鏡真的是非常笨重,裝上去以後,微單眼都不微單眼了。加上沒有內建閃燈,變成帶它出門一定要帶個專用相機包來放外閃(至少有熱靴)。如果還要帶個專用包包出去,那麼買單眼就好了,幹嘛考慮微單眼?

為什麼最後Panasonic GX1勝出?因為它是真正集輕巧及靈活性為一身的相機。有可變角度的內建閃燈(可以打跳燈)又有輕巧型的變焦鏡(Panasonic LUMIX G X 14-42mm F3.5-5.6),基本上這樣就可以放進隨身的包包或是外套口袋出門了。而它又提供了熱靴,所以一般的相機閃燈都可以使用。

有些人會說在一樣的預算下,Panasonic G3規格一模一樣,只大了一點點卻多了觀景窗跟翻轉螢幕,何樂而不「買」呢?其實很多人都忽略了一點,就是Panasonic G3在錄影的時候可以變換光圈大小,而GX-1不行。對於常用相機拍攝影片的人來說,G3會是個比GX1還好的選擇。但是G3就是高了那麼一點,厚了那麼一點,以我實際把玩之後,覺得已經有單眼而不是微單眼的感覺,至少一般的口袋放不進。

所以我剩下的朋友都選擇了Panasonic GX1(以目前的狀況,不考慮之後的新機型及新配件,US$440,約台幣一萬四千多,就有Panasonic GX-1加上Panasonic LUMIX G X 14-42mm F3.5-5.6的餅乾變焦鏡)。

這篇看起來好像業配文….

Wednesday, July 25, 2012

Get a peek at the unidentified items in Diablo 3

It has been almost 3 weeks before the trick was revealed, and Blizzard has, so far, done nothing to address this issue. Not that they did not have the resources. There were two major issues (Wizard & Barbarian being invincible) that were quickly nerfed within 24 hours, but Blizzard just does not care about the standard gaming experience, at least not their top priority; at least that's how they made me feel.

The item rolling is so random that I usually like to sell the items as unidentified rather than identifying them myself, but this bug completely destroyed the whole unidentified item market.

Let me, first, explain how the trick works.

1. Go to Auction House and open the inventory window.
2. Press ENTER to bring out the dialog prompt.
3. SHIFT-left click on the unidentified item
4. Enter two multibyte characters (Chinese, Japanese or just two Unicode symbols)
5. SHIFT-left click on the unidentified item again.
6. You prompt should look like: [Item]●●[Item] (● can be any multibyte characters)
7. Now, left-click in the middle of the ●● characters.



8. Press DELETE 3 times
9. Now, the internal code of the unidentified item is revealed.




Now, the string [Ring] Item:2,1146967347:935114366:1289779934,1599105366,-1976254572,-244372676:-1:0:130760:15:3:8:390:390:0:0:6:0:873858439:|h[Ring]|h may seem meaningless to you, but in fact that's all the information you need to figure out what the item is.

You can copy the string and paste it to a Diablo 3 Item Identification Tool (http://www.mynikko.com/d3/) to find out what the affixes are.

Fix it, please, Blizzard. It's only one if-statement.

Wednesday, July 4, 2012

You don't need RMAH to finish Diablo 3

I have read through most of reviews, both positive and negative, of Diablo 3. I must clarify one thing: you do not need to spend real money in order to finish the game (including Inferno level).

After 400 hours into this game, I did not spend a single extra penny. In fact, I added more than US$100 into my Blizzard account for future game purchases by selling items in RMAH.

Many people are complaining they cannot get the items they want, but that's how Auction House comes in handy. You got some items you do not want; you sell them at Auction House in exchange for buying something you want from there.

People are complaining the items they want are expensive, but your items can be sold at that price level, too. Blizzard cannot just give you great items; you have to hunt for them.

My character is capable of beating the Inferno level now, but I choose not too. I chose to farm items. Yes, its a repetitive process, but it has always been a major part of Diablo series.

As 1.0.3, all monsters at Inferno level have the possibility of dropping iLv61~63 items (just at different drop rates). One iLv62 rare (yellow) ring with good attributes can be sold for 20 millions or more of gold (or US$40+ in RMAH) if you hit the jackpot (e.g. IAS + Cri. DMG + Cri. % + Life on Hit). A good iLv63 weapon are even more expensive, I have seen weapons that are being sold for more than 100 million. I did not have such luck, but I did manage to get a couple rings that were sold for 5 million each and some for US$20 each in RMAH. With those money, I greatly improved my gears to a level that I can now make about 5 millions in a few hours by selling items. Even un-identified iLv62 rings can be sold for 1 million on the public trading channel. I get those rings everyday (at least one per day). The more those Chinese gold farmers/bots jack up the market, the better price you items will be sold.

I have to say the current Diablo 3 game system is flawed, but it would not be fair to say something like this game sucks because Blizzard forced gamers to spend money in RMAH in order to finish the game. If you do not want to spend time farming, then Diablo series is not for you in the first place. RMAH provides a short cut for people who have little time but are willing to pay the price. For most players, just listen for the drop sound that will change your gears/game entirely. Again, you do not need to spend more money if you do not want to.

Now, Blizzard is publicly announcing that they are thinking about changing the Magic Find (MF) mechanism so fast MF gear switching during combat will not be feasible. Again, this is why I mentioned in the previous review that Blizzard always tries to control the players so they play the way Blizzard wanted. While fast MF gear switching might seem unfair for melee classes, Blizzard could have introduced something that encourages players not to switch their gear during combat (e.g. if you don't switch your gears, there is a possibility that a goblin will pop out after the combat). Why does Blizzard always try to limit the players' playing style while there are more important things to do, like stopping those bots, or at least make them disappear from the public channel. Yes, there are bots, but at least don't let them remind me through public channels that it is totally stupid to farm because 1 million gold can be bought for less than US$2. What about legendary upgrades? How about make multi-player experience more enjoyable (right now, no one wants to play multi-player games)? How about some positive changes for the gaming experience? Encourage us instead of limiting/nerfing.

Blizzard, C'mon. You can do better than this.

PS. the above review is based on Patch 1.0.3b

Wednesday, June 27, 2012

Is Diablo 3 Another C&C4?

I know Diablo 3 and Command & Conquer 4 are two completely different games (even genres), but take a look at C&C4's reviews and you will find that both share the same characteristic: sequel of classic series got ruined by capitalist business giants (Blizzard/Activation and Westwood/EA). I am not saying D3 is dead but Blizzard should really listen and respect their customers.

First things first, I would rate this game around 7 out of 10 if this was not Diablo 3. The graphic, the music, voice acting and the design make it a decent game. However, I cannot believe this is a final product from the company that made the same D1 and D2. I would elaborate this later. I give it a 5/10 for being D3.

I have to admit that persistent internet connection requirement is sometimes unavoidable (e.g. MMORPG), but completely removing single player mode from D3? To me that is not acceptable at all. Players will still want to play on Battle.net eventually, so piracy prevention is not an excuse. Even StarCraft2 which is focused on online PvP has single player mode. One of the main reasons why I love single player mode is that I can change the game save files to experiment with different gears that is almost impossible for me to get online and test out what gears are best for my characters. They made the whole game completely Blizzard service dependent. I do not like the trend that games are no longer yours. Let's rate D2 and D3 hundred years from now. I will be able to play D2 even after years, but D3 is just a worthless game client without Battle.net of Blizzard. For this reason, I give this game 2/10 (No, 4 people in the same room does not count as MMORPG).

Now, I will explain why D3 game system is fundamentally flawed. RPG is about character development and gear collection. D3 completely neglected the first part. What so unique about your character in D3 are your name, class and gender. Every level 60 character of the same class is virtually the same without gears. The stat points are added for you automatically. The worse of all, the gears play too much role in the whole system. You do not feel your character is developing and growing. You level up simply because you want to meet the item level requirement. Why is this? Every time you level up, you get 1~3 stat point toward different categories (automatically added), depending on your character class, but one item that adds 100 strength and 100 vitality, which is common by the way, just makes your character looks like a fool. One piece of common item is almost 50 times more effective than leveling your characters.

DPS also played too much role. For example, a weapon with 1000DPS deals more damage than a 700DPS weapon that adds 150~200 points to main stat of your character. The DPS itself is more important than the stat, and the item stat is more important than your character. Your character becomes a meaningless skeleton that merely holds your gears together. The brainless DPS system also decreases the fun factor. The elemental nature of the weapons is almost meaningless, because weapons all deal the same damage regardless the nature of your enemy. They do have their differences, like cold can slow and poison has damage over time...etc, but I do not understand why Blizzard implemented the elemental system but decided to simplify it, especially when it was proven to be successful in D2.

Now, everyone is criticizing the Inferno level, but I actually like the design. The only mistake Blizzard made was to make it obvious so players think that they have to beat the Inferno level in order to "finish" the game. If inferno level can only be access through a secret portal, for example, Leah's bed after you finished the Hell difficulty, no one will complain as much. Inferno is like a challenge, for hardcore players to get great items, but not for average players to explore. Like Whimsyshire, no one would complain if the flowers are too strong, because it is a bonus level. In fact, not many players understand that you have to build up your all resistance to a certain point in order to "steal" items from those mobs. I do have to admit that Inferno was broken for melee class, but it was pretty OK for ranged class.

What made Inferno such a beast is, in fact, the fundamental flaw I mentioned earlier: this game is too gear oriented. If the stat of your characters plays about 75% of your overall damage/defense while the gear makes up the rest 25%. Inferno will be much more playable for players without gears. Now, the game design plus the difficulty of Inferno force players to spend gold in auction house. I do not know if this was an intentional design or not. If this was intended for making a profit, then Blizzard will see the profit goes way down because players are not that stupid, Mr. Obvious.

Those are just game design issues that can be addressed in future patches, although I do not know how Blizzard is going to be able to patch the gear oriented design. What disgusts me the most is the attitude of Blizzard: Blizzard created the game and wanted players to play the exactly way they wanted. I do have to admit that some of the nerfed skills are overpowered, but what about those underused skills? They nerfed some of the popular skills, but they did not think the reason behind it: why players do not choose other skills? They do not because those skills sucked. Period. The shameless person who said D3 does not need customize stat build because it got countless skill combination should be working for tobacco marketing companies.

1.0.3 is an arrogant display of Blizzard; they force you to play this game exactly they wish you to play. Yes, there are some positive changes (at least melee class can access Inferno Act3~4), but the rush changes clearly showed that they do not care about player experience. For example, they changed the drop rate and magic find for chests, and now they are thinking about changing it back. People are being told what you need to be doing in school/at work, and they do not like the same experience in game. This clearly showed that there is something wrong with Blizzard's QA department.

It is extremely easy for Blizzard to test out if the gears have the potential to be overpowered by giving QA testers the best gear within the game design BEFORE the game release. The players are not QA, and players should not be the victims of consequence of those irresponsible patch changes. As a game designer myself though, I think Blizzard needs to fire some of the management directors (or just fire one, the rest will learn). This is clearly a management/direction issue. They are responsible for those irresponsible changes. If Blizzard think twice, they could have buff some of the underused skills and classes instead of nerfing the difficulty. I do not know about others but I found the game much more boring because there is nothing left for me to do now.

It is ironic that I typed so much for a game that I dislike so much. No, it is not. I like this game; I want D3 to be another classic. I just hope people in Blizzard have the same motivation and desire to do so.

P.S. The above review is based on Patch 1.0.3a.

Monday, June 25, 2012

The annoying templar, Kormac (Diablo 3)

It's funny that many players complained about how annoying those followers are in the official Blizzard forum. I actually liked a lot of their dialogues. However, I could not find a complete list of D3 follower quotes online.


I did find this funny comic, which I did a (nice?) little translation.




Wednesday, March 21, 2012

Easiest Way to Disable Google Chrome Auto Update


Any program trying to be smart is stupid. Google Chrome is one of them. Its aggressive automatic update mechanism just makes me sick (same as Adobe family). For me, any ”automatic” action without my consent is considered offensive viral behavior. So I used the same method against Google Chrome.






First, remove any Scheduled Tasks placed by Google Chrome which can be found in the control panel.

Second, rename the auto-update program.

Let’s use the god damned Google Chrome as our example.

For Windows XP, go to the following directory (if you cannot find the ‘Local Settings’ directory, you need to enable the show hidden file option):

C:\Documents and Settings\[User Name]\Local Settings\Application Data\Google\Update\


Rename GoogleUpdate.exe to something like GoogleUpdate.GoToHell.

You can also delete it if you want, but let’s just save it just in case you need to update the software later.

Third, the most important part of the while operation: make a directory named “GoogleUpdate.exe

This way, Google Chrome will not be able to create the file named GoogleUpdate.exe. Thus, prevent it from trying to enable the update itself.

In short, the above operations in terms of DOS commands are:
C:\>CD "C:\Documents and Settings\[User Name]\Local Settings\Application Data\Google\Update"

C:\Documents and Settings\[User Name]\Local Settings\Application Data\Google\Update\> ren GoogleUpdate.exe GoogleUpdate.GoToHell

C:\Documents and Settings\[User Name]\Local Settings\Application Data\Google\Update\> mkdir "GoogleUpdate.exe"


That’s it. To revert the whole process, simply remove the “GoogleUpdate.exe” directory and rename “GoogleUpdate.GoTOHell” back to “GoogleUpdate.exe”

Easy enough right? You can use the same trick for those auto-update-maniac programs including, but not limited to, Adobe Photoshop, Acrobat Reader…etc. Some programs, like Adobe malware, even place start up processes which runs automatically every time your computer starts. You might want to remove them from the autostart list by cleaning the autostart folder and msconfig.exe (in Windows case).

Tuesday, March 13, 2012

Auto-increment ID Obfuscation

One problem I encountered is that an auto incremented ID may expose certain information that you do not want your clients to know.

For example, http://www.company.com/order?id=1234 tells you that this company probably does not have a lot of new orders since your order probably is the 1234th of their overall orders. http://www.NewDatingSite.com/member?id=256 is simply telling people that you probably will not be able to find lots new matches since the newest member is just their 256th member.

Of course you can encrypt the ID number to make it more complicated like http://www.ConfusingID.com/stuff?id=!W%23R%5E%25YFT%5E. While this ensures that your customers probably will not be able to probe around and know that they are actually your first ever guinea pig customer, they will definitely have a fun time reading that ID to your service representative when calling over the phone. That ID is simply too ugly for the eyes.

You can also create a 1-to-1 hash table (beware of the collisions) or use some mathematical skills to create a mapping algorithm, but that is just too complicated for me.

I will just present the approach I am currently using. One of the main ingredients is base32 encode. Base32 encode has one major advantage over Base64 encode, that is: base32 is URL ready; it does not require additional encoding for URL while Base64 uses '=' character, which will need to be URL encoded as '%3D'.

Let's just see the two main functions:
function id_encode($id) {
return base_convert((9999999999 - (float) $id) , 10 , 32 );
}

function id_decode($fakeid) {
return 9999999999 - (float) base_convert($fakeid, 32, 10);
}


Let's use some numbers to run the following code:
<?
printf("%s → %s → %s <br />", $test, id_encode($test), id_decode(id_encode($test)) );
?>


The output would be:
1 → ghq6n2 → 1 
2 → ghq6n1 → 2
3 → ghq6n0 → 3
100 → ghq6jv → 100
1000 → ghq5nr → 1000
10000 → ghpsuj → 10000
1000000 → ggrm53 → 1000000
94256245 → dntnje → 94256245


So instead of having http://www.mycompany.com/order?id=3, the new URL will be: http://www.mycompany.com/order?id=ghq6n0. Yes, the ID is longer, but the length is fixed. One of the reasons why we need a fixed length ID is that user will have no way to tell the relative position of the ID in the whole database.If you do not need to have fixed length, just remove the minus 9999999999 part, whose purpose is just for fixed length padding.

It is pretty simple right? If you don't need encryption, you can simply stop reading right here.



Now, some people might need some simple encryption. Let's throw in xor and base32_encode (base32_encode is needed because string after xor might not be a printable char). Of course, if you intentionally choose your xor key, you can even make the ID resemble a meaningful string (but will gradually obfuscate as the number grows larger and reveal the fact that the number is growing). The following is an example with xor encryption.

1 → mycompanynbq → 1
2 → mycompanynba → 2
3 → mycompanyncq → 3
100 → mycompanyzca → 100
1000 → mycompanqvma → 1000
10000 → mycompaeqjia → 10000
1000000 → mzokais22faa → 1000000
94256245 → mzjoaic3yzlq → 94256245




Another encrypted example without padding:
1 → ny → 1
2 → nu → 2
3 → nq → 3
100 → nriq → 100
1000 → ffoq → 1000
10000 → myflc → 10000
1000000 → fibl6yq → 1000000
94256245 → nuk2kncr3e → 94256245



Now let's examine the xor_this function, it's also pretty short (and it's actually an example from php.net):

<?php
function xor_this($string) {
$key = 'JohnyBeGood';

$text = sprintf('%s', $string);

$outText = '';

for($i=0;$i<strlen($text);) {
for($j=0;$j<strlen($key);$j++,$i++) {
$outText .= $text{$i} ^ $key{$j};
}
}
return $outText;
}
?>


Finally you need the base32_encode and base32_decode functions. Interestingly, PHP offers base64 encode/decode, but not base32. You can Google a bit and find it right around here: http://xref.moodle.org/nav.html?lib/base32.php.source.html

For those who are too lazy to click (or the above link is down for some reason), I will just paste everything here:
<?php
//
// +----------------------------------------------------------------------+
// | Base32 Library |
// +----------------------------------------------------------------------+
// | Copyright (c) 2001 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is dual-licensed. It is available under the terms |
// | of the GNU GPL v2.0 and under the terms of the PHP license version |
// | 2.02, available at through the world-wide-web at |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// +----------------------------------------------------------------------+
// | Minor fixes and additional functions by Allan Hansen. |
// | Moodle porting work by Martin Langhoff |
// +----------------------------------------------------------------------+
// | base32.php - based on race.php - RACE encode and decode strings. |
// +----------------------------------------------------------------------+
// | Authors: Allan Hansen <All@nHansen.dk> |
// | Arjan Wekking <a.wekking@synantics.nl> |
// | Martin Langhoff <martin@catalyst.net.nz> |
// +----------------------------------------------------------------------+
//

/**
* Base32 encode a binary string
*
* @param $inString Binary string to base32 encode
*
* @return $outString Base32 encoded $inString
*
* @access private
*
*/

function base32_encode($inString) {
$outString = "";
$compBits = "";
$BASE32_TABLE = array('00000' => 0x61, '00001' => 0x62, '00010' => 0x63, '00011' => 0x64, '00100' => 0x65, '00101' => 0x66, '00110' => 0x67, '00111' => 0x68, '01000' => 0x69, '01001' => 0x6a, '01010' => 0x6b, '01011' => 0x6c, '01100' => 0x6d, '01101' => 0x6e, '01110' => 0x6f, '01111' => 0x70, '10000' => 0x71, '10001' => 0x72, '10010' => 0x73, '10011' => 0x74, '10100' => 0x75, '10101' => 0x76, '10110' => 0x77, '10111' => 0x78, '11000' => 0x79, '11001' => 0x7a, '11010' => 0x32, '11011' => 0x33, '11100' => 0x34, '11101' => 0x35, '11110' => 0x36, '11111' => 0x37);

/* Turn the compressed string into a string that represents the bits as 0 and 1. */
for ($i = 0; $i < strlen($inString); $i++) {
$compBits .= str_pad(decbin(ord(substr($inString,$i,1))), 8, '0', STR_PAD_LEFT);
}

/* Pad the value with enough 0's to make it a multiple of 5 */
if((strlen($compBits) % 5) != 0) {
$compBits = str_pad($compBits, strlen($compBits)+(5-(strlen($compBits)%5)), '0', STR_PAD_RIGHT);
}

/* Create an array by chunking it every 5 chars */
$fiveBitsArray = split("\n",rtrim(chunk_split($compBits, 5, "\n")));

/* Look-up each chunk and add it to $outstring */
foreach($fiveBitsArray as $fiveBitsString) {
$outString .= chr($BASE32_TABLE[$fiveBitsString]);
}

return $outString;
}

function Base32_decode($inString) {
/* declaration */
$inputCheck = null;
$deCompBits = null;

$BASE32_TABLE = array(0x61 => '00000', 0x62 => '00001', 0x63 => '00010', 0x64 => '00011', 0x65 => '00100', 0x66 => '00101', 0x67 => '00110', 0x68 => '00111', 0x69 => '01000', 0x6a => '01001', 0x6b => '01010', 0x6c => '01011', 0x6d => '01100', 0x6e => '01101', 0x6f => '01110', 0x70 => '01111', 0x71 => '10000', 0x72 => '10001', 0x73 => '10010', 0x74 => '10011', 0x75 => '10100', 0x76 => '10101', 0x77 => '10110', 0x78 => '10111', 0x79 => '11000', 0x7a => '11001', 0x32 => '11010', 0x33 => '11011', 0x34 => '11100', 0x35 => '11101', 0x36 => '11110', 0x37 => '11111');

/* Step 1 */
$inputCheck = strlen($inString) % 8;
if(($inputCheck == 1)||($inputCheck == 3)||($inputCheck == 6)) {
trigger_error('input to Base32Decode was a bad mod length: '.$inputCheck);
return false;
}

/* $deCompBits is a string that represents the bits as 0 and 1.*/
for ($i = 0; $i < strlen($inString); $i++) {
$inChar = ord(substr($inString,$i,1));
if(isset($BASE32_TABLE[$inChar])) {
$deCompBits .= $BASE32_TABLE[$inChar];
} else {
trigger_error('input to Base32Decode had a bad character: '.$inChar);
return false;
}
}

/* Step 5 */
$padding = strlen($deCompBits) % 8;
$paddingContent = substr($deCompBits, (strlen($deCompBits) - $padding));
if(substr_count($paddingContent, '1')>0) {
trigger_error('found non-zero padding in Base32Decode');
return false;
}

/* Break the decompressed string into octets for returning */
$deArr = array();
for($i = 0; $i < (int)(strlen($deCompBits) / 8); $i++) {
$deArr[$i] = chr(bindec(substr($deCompBits, $i*8, 8)));
}

$outString = join('',$deArr);

return $outString;
}

?>
<?php
// +----------------------------------------------------------------------+
// | Auto-increment ID Obfuscation |
// +----------------------------------------------------------------------+
// | Copyright (c) 2012 MyNikko.com |
// |http://mynikko.blogspot.com/2012/03/auto-increment-id-obfuscation.html|
// +----------------------------------------------------------------------+
function xor_this($string) {
$key = 'JohnnyBeGood';

$text = sprintf('%s', $string);

$outText = '';

for($i=0;$i<strlen($text);) {
for($j=0;$j<strlen($key);$j++,$i++) {
$outText .= $text{$i} ^ $key{$j};
}
}
return $outText;
}

function id_encode($id) {
return base32_encode(xor_this(base_convert(((float) $id) , 10 , 32 )));
}

function id_decode($fakeid) {
return (float) base_convert(xor_this(base32_decode($fakeid)), 32, 10);
}

$test = "1";
printf("%s → %s → %s <br />", $test, id_encode($test), id_decode(id_encode($test)) );
$test = "2";
printf("%s → %s → %s <br />", $test, id_encode($test), id_decode(id_encode($test)) );
$test = "3";
printf("%s → %s → %s <br />", $test, id_encode($test), id_decode(id_encode($test)) );
$test = "100";
printf("%s → %s → %s <br />", $test, id_encode($test), id_decode(id_encode($test)) );
$test = "1000";
printf("%s → %s → %s <br />", $test, id_encode($test), id_decode(id_encode($test)) );
$test = "10000";http://www.blogger.com/img/blank.gif
printf("%s → %s → %s <br />", $test, id_encode($test), id_decode(id_encode($test)) );
$test = "1000000";
printf("%s → %s → %s <br />", $test, id_encode($test), id_decode(id_encode($test)) );
$test = "94256245";
printf("%s → %s → %s <br />", $test, id_encode($test), id_decode(id_encode($test)) );
?>



This actually answers the question here: http://stackoverflow.com/questions/432291/1-1-mappings-for-id-obfuscation