#!/bin/bash
##
##  @author: Benjamin Berteaux (obibann)
##  @contact: obibann@hotmail.com
##  @description: allows to hide / unhide a wii u device. Trick compatible with latest versions of Loader GX.
##  @version: 1.0.0.0
##

# ******************************************************************
# GLOBAL VARS BEGIN
# ******************************************************************
SEL_DEVICE=
DEFAULT_VALUE="\x55\xAA"          # Default value of bytes 511 & 512
HIDE_VALUE="\x55\xAB"             # Hide value of bytes 511 & 512
# ******************************************************************
# GLOBAL VARS END
# ******************************************************************

# ******************************************************************
# FUNCTION LIST BEGIN
# ******************************************************************

## /**
##  * Checks if a command is present
##  *
##  * @param[in] $1 command
##  *
##  * Returns 0 if ok, > 0 otherwise
##  */
check_req() {

  if ! which $1 > /dev/null 2>&1 ; then
    echo "Could not find program $1"
    exit 1
  fi

}

## /**
##  * Tells if a device is hidden or not
##  *
##  * @param[in] $1 device
##  *
##  * Returns 1 if hidden, 0 otherwise
##  */
is_hidden() {

  hidden=1
  csign=$(dd if=$1 bs=1 skip=510 count=2 2>/dev/null | hexdump | head -1 | awk '{print $2}')
  [ "$csign" == "aa55" ] && hidden=0
  return $hidden

}

## /**
##  * Displays device model
##  *
##  * @param[in] $1 device
##  *
##  * echoes device model
##  */
device_model() {

  parted -l $1 | head -1 | cut -d":" -f2-

}

## /**
##  * Displays device size
##  *
##  * @param[in] $1 device
##  *
##  * echoes device size
##  */
device_size() {

  parted -l $1 | sed -n "2,2p" | egrep -o "[0-9]+[KMGT]B"

}

## /**
##  * Erases bytes 511 and 512 of given device
##  *
##  * @param[in] $1 device
##  * @param[in] $2 new value
##  *
##  * Returns 1 if something went wrong, 0 otherwise
##  */
alter_mbr_sign() {

  # Dumping MBR
  dd if=$1 of=/tmp/hidedisk_mbr.dump bs=512 count=1 > /dev/null 2>&1
  [ $? -ne 0 ] && return 1

  # Altering dump
  (dd if=/tmp/hidedisk_mbr.dump bs=1 count=510 2>/dev/null ; echo -en $2)>/tmp/hidedisk_mbr_alt.dump
  [ ! -s /tmp/hidedisk_mbr_alt.dump ] && return 2

  # Erasing MBR
  dd if=/tmp/hidedisk_mbr_alt.dump of=$1 bs=512 count=1 > /dev/null 2>&1
  [ $? -ne 0 ] && return 3

  # Syncronizing
  sync

  # Cleaning...
  rm -f /tmp/hidedisk_mbr_alt.dump /tmp/hidedisk_mbr.dump

  # All ok
  return 0

}

## /**
##  * Hides a device
##  *
##  * @param[in] $1 device
##  *
##  * Stopps if something went wrong
##  */
hide() {

	alter_mbr_sign "$1" "$HIDE_VALUE"
  if [ $? -ne 0 ] ; then
    zenity --error --text "Could not hide disc"
    exit 1
  fi

}

## /**
##  * Unhides a device
##  *
##  * @param[in] $1 device
##  *
##  * Stopps if something went wrong
##  */
unhide() {

	alter_mbr_sign "$1" "$DEFAULT_VALUE"
  if [ $? -ne 0 ] ; then
    zenity --error --text "Could not unhide disc"
    exit 1
  fi

}

# ******************************************************************
# FUNCTION LIST END
# ******************************************************************

# ******************************************************************
# MAIN BEGIN
# ******************************************************************

# CHECKING REQUIREMENTS
check_req "bash"
check_req "dd"
check_req "zenity"
check_req "gksudo"
check_req "parted"

# ROOT ONLY
if [ "$(whoami)" != "root" ] ; then
	gksudo $0
	exit 0
fi

# Fetching device list
devices=
while read mydisc
do

	mounted=$(df -P | egrep -c "^${mydisc}")
	if [ "$mounted" == "0" ] ; then
		devstat="No"
    dev_model=$(device_model "$mydisc")
    dev_size=$(device_size "$mydisc")
		is_hidden "$mydisc" ; [ $? -eq 1 ] && devstat="Yes"
		devices+="$mydisc \"$dev_model\" $dev_size $devstat "
	fi

done < <(ls /dev/sd[a-z])

# No device detected
if [ -z "$devices" ] ; then
	zenity --error --text "No device detected. Please unmount your disc and retry."
	exit 1
fi

# Device List
choice=$(eval "zenity --height=200 --width=600 --title='Device selection' --text='Please select a device to hide/unhide' --list --column=Device --column=Model --column=Size --column=Hidden $devices")
choice=$(echo $choice | cut -d"|" -f1)

# User canceled or chose nothing
if [ -z "$choice" ] ; then
	zenity --error --text "No device selected. Exiting."
	exit 1
fi

# Interpreting user choice
SEL_DEVICE=$choice
is_hidden $SEL_DEVICE ; DEVSTAT=$?
statmsg="unhidden"
[ "$DEVSTAT" == "0" ] && statmsg="hidden"

# Confirmation
zenity --question --text="Device is about to be $statmsg.\nMake sure you have backuped your data.\nContinue ?"

# Processing !!
if [ $? -eq 0 ] ; then

  # Hide / unhide
	[ "$DEVSTAT" == "0" ] && hide $SEL_DEVICE
	[ "$DEVSTAT" != "0" ] && unhide $SEL_DEVICE
  
  # End
  zenity --info --text "$SEL_DEVICE is now $statmsg"
fi

# ******************************************************************
# MAIN END
# ******************************************************************
