Skip to content

Instantly share code, notes, and snippets.

@carloshm
Forked from juster/vce.tcl
Created February 5, 2023 10:54
Show Gist options
  • Save carloshm/b9d59da4ea0da6d984ef94d240aca052 to your computer and use it in GitHub Desktop.
Save carloshm/b9d59da4ea0da6d984ef94d240aca052 to your computer and use it in GitHub Desktop.
VCE Exam Taker
package require Tk
proc initTop {} {
menu .topMenu
menu .topMenu.fileMenu -tearoff 0
.topMenu.fileMenu add command -label Open... -command selectVCEPath
.topMenu add cascade -label File -menu .topMenu.fileMenu
. configure -menu .topMenu
grid [ttk::frame .top] -row 1 -column 1 -sticky we
grid [ttk::frame .mid] -row 2 -column 1 -sticky we
## Title
set p .top
grid [ttk::label $p.titLbl -text Title] -row 1 -column 1
grid [ttk::entry $p.titEnt -state readonly -textvariable vceTitle] \
-row 1 -column 2 -sticky we
## Current question count.
grid [ttk::label $p.curLbl -text Current] -row 2 -column 1
grid [ttk::entry $p.curEnt -state readonly -textvariable vceCurrent] \
-row 2 -column 2 -sticky we
## Next/previous buttons
grid [ttk::button $p.nextBtn -text Next -command { gotoNextQ }] \
-row 1 -column 3
grid [ttk::button $p.prevBtn -text Prev -command { gotoPrevQ }] \
-row 2 -column 3
## Question frame
set p .mid
text $p.qTxt -height 5 -wrap word -state disabled -yscrollcommand "$p.qScrl set"
grid $p.qTxt -row 1 -column 1 -sticky ew
ttk::scrollbar $p.qScrl -command "$p.qTxt yview"
grid $p.qScrl -row 1 -column 2 -sticky nes
ttk::button $p.answerBtn -command submitAnswer -text Answer
grid $p.answerBtn -row 2 -column 1 -columnspan 2 -sticky w
grid columnconfigure . 1 -weight 1
grid columnconfigure .top 2 -weight 1
grid columnconfigure .mid 1 -weight 1
}
proc selectVCEPath {} {
global vcePath
set path [tk_getOpenFile]
if { $path eq {} } { return }
set vcePath $path
openVCE $path
}
proc openVCE {path} {
global vceChan vceVer vceTitle vceCount vceQStart vceIndex
set vceChan [open $path {RDONLY BINARY}]
binary scan [read $vceChan 4] a2cc magic hiVer loVer
if { $magic ne "\x85\xA8" } {
tk_messageBox -icon error -message {File does not appear to be in the VCE format.}
close $vceChan
return
}
set vceVer "$hiVer.$loVer"
skip $vceChan 1
set examCode [readXStr $vceChan]
set examTitle [readXStr $vceChan]
set vceTitle "$examCode - $examTitle"
## Not sure WTF?
skip $vceChan [expr {16 * 3 + 5}]
## Decodes to all zeros?
skipXStr $vceChan
## Big JPEG and stuff. Intro message.
skipXStr $vceChan
skip $vceChan 9
binary scan [read $vceChan 4] i vceCount
set vceQStart [tell $vceChan]
set vceIndex 1
scanQ $vceChan
return
}
proc skip {ch count} {
seek $ch $count current
}
proc skipXStr {ch} {
set count 0
binary scan [read $ch 4] i count
if { $count != 0 } {
skip $ch $count
}
}
proc readXStr {ch} {
binary scan [read $ch 4] i count
# & makes unsigned
set count [expr {$count & 0xFFFFFFFF}]
set msg [read $ch $count]
decXOR msg
return $msg
}
proc decXOR {bname} {
upvar 1 $bname bytes
set i 0
set msg {}
binary scan $bytes c* blist
foreach b $blist {
set x [expr {($b ^ $i) & 0xFF}]
append msg [format %c $x]
incr i
}
set bytes $msg
}
#####################################################################################################
proc gotoPrevQ {} {
global vceIndex vceChan
if { $vceIndex <= 1 } {
tk_messageBox -icon error -message "No previous question exists."
} else {
incr vceIndex -1
gotoQ $vceChan $vceIndex
}
}
proc gotoNextQ {} {
global vceIndex vceChan vceCount
if { $vceIndex >= $vceCount } {
tk_messageBox -icon error -message "No next question exists."
} else {
incr vceIndex
gotoQ $vceChan $vceIndex
}
}
proc gotoQ {ch idx} {
global vceCount vceQStart
if { $idx < 1 } {
error "Question index cannot be less than one."
} elseif { $idx > $vceCount } {
error "Index is greater than question count."
}
seek $ch $vceQStart
incr idx -1
for { set i 0 } { $i < $idx } { incr i } {
skipQ $ch
}
scanQ $ch
}
proc skipQ {ch} {
scanQStart $ch
skip $ch 8
skipXStr $ch
skipXStr $ch
skip $ch 7
skipXStr $ch
}
#########################################################################################
proc scanQ {ch} {
global vceCurrent vceIndex vceCount
set offset [tell $ch]
set vceCurrent "$vceIndex/$vceCount (0x[format %X $offset])"
.top.nextBtn configure -state \
[expr { $vceIndex >= $vceCount ? "disabled" : "enabled" }]
.top.prevBtn configure -state \
[expr { $vceIndex <= 1 ? "disabled" : "enabled" }]
global vceMulti vceChoiceCount
destChoices $vceMulti $vceChoiceCount
set vceMulti [scanQStart $ch]
skip $ch 8
scanQText $ch $vceMulti
global vceAnswer
set vceAnswer [readXStr $ch]
skip $ch 7
skipXStr $ch
}
proc scanQStart {ch} {
binary scan [read $ch 5] ca4 multi magic
if { $magic ne "\xFF\xFF\xFF\xFF" } {
error {This does not appear to be a question entry.}
} elseif { $multi > 1 } {
error "Question entry has an unknown multi flag: $multi"
}
return [expr {$multi == 1}]
}
proc scanQText {ch multi} {
set in [readXStr $ch]
global vceChosen vceChoiceCount
set vceChoiceCount 0
set vceChosen ""
set qtxt [scanQTextStr in]
.mid.qTxt configure -state normal
.mid.qTxt delete 0.0 end
.mid.qTxt insert 0.0 $qtxt
.mid.qTxt configure -state disabled
# .mid.answerBtn configure -state disabled
set i 0
while { [string length $in] > 0 } {
set txt [scanQTextStr in]
if { $txt eq {} } { break }
addChoice $multi $i $txt
incr i
}
set vceChoiceCount $i
}
proc scanQTextStr {strname} {
upvar 1 $strname str
binary scan [string range $str 0 3] i len
set txt [string range $str 4 [expr {$len + 4 - 1}]]
set str [string range $str [expr {$len + 4}] end]
regsub -all "\x0D" $txt {} txt
set lines [split $txt "\n"]
set txt [join [lrange $lines 2 end] "\n"]
regsub -all "\n*$" $txt {} txt
return $txt
}
#########################################################################################
proc indexLtr {idx} {
scan A %c a
return [format %c [expr {$a + $idx}]]
}
proc addChoice {multi idx txt} {
set ltr [indexLtr $idx]
set name ".mid.choice$ltr"
global vceChosen$ltr
if { $multi } {
set vceChosen$ltr 0
ttk::checkbutton $name -text "$ltr. $txt" -variable vceChosen$ltr
} else {
unset vceChosen$ltr
ttk::radiobutton $name -text "$ltr. $txt" -value $ltr -variable vceChosen
}
set row [expr {$idx + 3}]
grid $name -row $row -column 1 -sticky w
}
proc destChoices {multi count} {
foreach w [winfo children .mid] {
if { [string match .mid.choice* $w] } { destroy $w }
}
if { $multi } {
for { set i 0 } { $i < $count } { incr i } {
set ltr [indexLtr $i]
global vceChosen$ltr
unset vceChosen$ltr
}
}
}
proc getChosen {} {
set selected {}
global vceMulti
if { $vceMulti } {
global vceChoiceCount
for { set i 0 } { $i < $vceChoiceCount } { incr i } {
set ltr [indexLtr $i]
global vceChosen$ltr
if { [subst "\$vceChosen$ltr"] == 1 } {
append selected $ltr
}
}
} else {
global vceChosen
set selected $vceChosen
}
return $selected
}
proc submitAnswer {} {
global vceAnswer
set chosen [getChosen]
if { $chosen eq $vceAnswer } {
gotoNextQ
return
}
switch $vceAnswer {
"" {
tk_messageBox -icon error \
-message "Answer is empty, may not be read properly"
}
default {
tk_messageBox -message "Incorrect!" }
}
}
set vceIndex 0
set vceMulti False
initTop
selectVCEPath
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment