ZH's software thread

Place to talk about all that new hardware and decaying software you have.

Moderator: General Mods

Post Reply
h4tred

Post by h4tred »

For the people that give a fucking damn.......

Code: Select all

**********************************************************************************************
;* Example (how to use)                                                                       *
;* ------------------------------------------------------------------------------------------ *
;* search : 2A 45 EB ?? C3 ?? EF                                                              *
;* replace: 2A ?? ?? 10 33 C0 ??                                                              *
;*                                                                                            *
;* .data                                                                                      *
;* SearchPattern   db 02Ah, 045h, 0EBh, 000h, 0C3h, 000h, 0EFh                                *
;* SearchMask      db    0,    0,    0,    1,    0,    1,    0	 ;(1=Ignore Byte)             *
;*                                                                                            *
;* ReplacePattern  db 02Ah, 000h, 000h, 010h, 033h, 0C0h, 000h                                *
;* ReplaceMask     db    0,    1,    1,    0,    0,    0,    1	 ;(1=Ignore Byte)             *
;*                                                                                            *
;* .const                                                                                     *
;* PatternSize     equ 7                                                                      *
;*                                                                                            *
;* .code                                                                                      *
;* push -1                      ;Replace Number (-1=ALL / 2=2nd match ...)                    *
;* push FileSize                ;how many bytes to search from beginning from TargetAdress    *
;* push PatternSize             ;lenght of Pattern                                            *
;* push offset ReplaceMask                                                                    *
;* push offset ReplacePattern                                                                 *
;* push offset SearchMask                                                                     *
;* push offset SearchPattern                                                                  *
;* push TargetAddress           ;the memory address where the search starts                   *
;* call SearchAndReplace                                                                      *
;*                                                                                            *
;* ReturnValue in eax (1=Success 0=Failed)                                                    *
;**********************************************************************************************



.586					
.model flat, stdcall
option casemap :none

SearchAndReplace PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD

.code
;----this procedure is only for compiling a dll---
align 16
DllEntry proc _hinstance:DWORD, _reason:DWORD, _reserved1:DWORD
	mov eax,1 ;TRUE
	ret
DllEntry endp


align 16
SearchAndReplace proc 	_targetadress:dword,_searchpattern:dword,_searchmask:dword,_replacepattern:dword,
			_replacemask:dword,_patternsize:dword,_searchsize:dword,_patchnumber:dword
			
	LOCAL local_returnvalue	:byte 	;returns if something was patched
	LOCAL local_match	:dword	;counts how many matches
	
	pushad
	mov local_returnvalue,0
	mov local_match,0
	
	mov edi,_targetadress
	mov esi,_searchpattern
	mov edx,_searchmask
	mov ebx,_patternsize
	xor ecx,ecx
	
	.while ecx!=_searchsize
		@search_again:
		;---check if pattern exceed memory---
		mov eax,ecx		;ecx=raw offset
		add eax,ebx		;raw offset + patternsize
		cmp eax,_searchsize
		ja @return		;if (raw offset + patternsize) > searchsize then bad!
		
		push ecx		;counter
		push esi		;searchpattern
		push edi		;targetaddress
		push edx		;searchmask
		
		mov ecx,ebx		;ebx=patternsize
		@cmp_mask:
		test ecx,ecx
		je @pattern_found
		cmp byte ptr[edx],1	;searchmask
		je @ignore
		lodsb			;load searchbyte to al & inc esi
		scasb			;cmp al,targetadressbyte & inc edi
		jne @skip
		inc edx			;searchmask
		dec ecx			;patternsize
		jmp @cmp_mask
		@ignore:
		inc edi			;targetadress
		inc esi			;searchpattern
		inc edx			;searchmask
		dec ecx			;patternsize
		jmp @cmp_mask
		
		@skip:
		pop edx
		pop edi			;targetadress
		pop esi			;searchpattern
		pop ecx
		
		inc edi			;targetadress
		inc ecx			;counter
	.endw
	;---scanned whole memory size---
	jmp @return	

	@pattern_found:
	inc local_match
	pop edx
	pop edi				;targetadress
	pop esi
	mov eax,_patchnumber
	cmp eax,-1
	je @replace			
	cmp local_match,eax
	je @replace
	pop ecx				;counter
	inc edi				;targetadress
	jmp @search_again
	
	;---replace pattern---
	@replace:
	mov esi,_replacepattern
	mov edx,_replacemask
	
	xor ecx,ecx
	.while ecx!=ebx			;ebx=patternsize
		@cmp_mask_2:
		cmp byte ptr[edx],1
		je @ignore_2
		lodsb			;load replacebyte to al from esi & inc esi
		stosb			;mov byte ptr[edi],al & inc edi
		jmp @nextbyte
		@ignore_2:
		inc edi			;targetadress
		inc esi			;replacepattern
		@nextbyte:
		inc edx			;replacemask
		inc ecx			;counter
	.endw
	mov local_returnvalue,1		;yes, something was patched
	
	;---search again?---
	pop ecx				;counter-->scanned size
	cmp _patchnumber,-1
	jne @return
	sub edi,ebx			;edi=targetadress ; countinue where stopped
	inc edi				;...
	inc ecx				;ecx=counter(pointer to offset)  /bug fixed in v2.07
	mov esi,_searchpattern
	mov edx,_searchmask
	jmp @search_again

	;---return---
	@return:
	popad
	movzx eax,local_returnvalue
	ret
SearchAndReplace endp

end DllEntry
Really neat ? At one point it gets too easy to be worth that praise, no ? ¬_¬
I am sorry if you are unable to see the beauty in pure ASM and what binary search and replace is really useful for. >.>

but meh, im not going to argue anymore about this.
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

h4tred wrote:
BTW Mudlord, most good hex editors contain search and replace of bytes.
I know, but I was referring for a way to do it pragmatically in C#/Visual Basic.NET. As in, you open a executable for binary read/update, scan for a particular byte pattern, and replace it with a different one. Should be quite obvious why such code is useful to some people....... :)
Easy to turn that into a search and replace too.
Yep, there is also code around to scan memory for bytes and replacing in real time. Also I have some assembly code for binary search and replace. Really neat stuff :).
If you remember that program I made to protect us from certain issues, it opens the binary and does a search and replace, since it doesn't know where the block of code will end up after compile. IIRC, I gave you the functions I made for S&R.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
grinvader
ZSNES Shake Shake Prinny
Posts: 5632
Joined: Wed Jul 28, 2004 4:15 pm
Location: PAL50, dood !

Post by grinvader »

h4tred wrote:
Really neat ? At one point it gets too easy to be worth that praise, no ? ¬_¬
I am sorry if you are unable to see the beauty in pure ASM and what binary search and replace is really useful for. >.>

but meh, im not going to argue anymore about this.
Don't go putting words in my mouth now.
... so yeah, way to get confused and miss the point so bad you actually got things completely reversed. Go you.

Once more with feeling.
A simple search and replace is TOO FUCKING SIMPLE. Beauty is complex. There's no beauty in 'hello world' or just searching bytes,
I can see the "Beauty in pure ASM" in things that are actually long enough to be beautiful.

ASM isn't like C, where I find the beauty in increasing the "things done per line" ratio.
An ASM line is always just a line and does little by itself. You can't have a beautiful line, because it's just one line and does jack.
ASM beauty is having a tiny amount of lines for a COMPLEX result. There's only so many ways to make a byte-searching loop in ASM that aren't complete stupidity or intended obfuscation.

Anyway... your code is more than a simple search and replace. That's the complexity I'm talking about. From a set point, adding new features or improving on the existing ones increases the complexity. Dealing with that elegantly is where beauty is in ASM.
皆黙って俺について来い!!

Code: Select all

<jmr> bsnes has the most accurate wiki page but it takes forever to load (or something)
Pantheon: Gideon Zhi | CaitSith2 | Nach | kode54
ZH/Franky

Post by ZH/Franky »

Got set some homework today. It was to write a program that checks the validity of an ISBN-13 number. Here's what I wrote:

Code: Select all

Module Module1

    Private isbn As String
    Private cu, stuff(13), total, check As Double
    Private mu As Boolean

    Sub Main()
        validate_isbn13()
        calculate_check()
        results()
        Console.ReadLine()
    End Sub

    Private Sub validate_isbn13()
        Console.Write("Enter ISBN-13 number: ")
        isbn = Console.ReadLine()
        If isbn.Length <> 13 Then
            Console.WriteLine("Invalid input; ISBN-13 number entered is not 13 digits")
            validate_isbn13()
        End If
        For pos = 1 To 13 Step 1
            Try
                stuff(pos) = Convert.ToInt32(Mid(isbn, pos, 1))
            Catch ex As Exception
                Console.WriteLine("Invalid input; some of the digits are not digits")
                validate_isbn13()
            End Try
        Next
    End Sub

    Private Sub calculate_check()
        mu = False
        For pos = 1 To 12 Step 1
            If mu = False Then
                mu = True
            ElseIf mu = True Then
                stuff(pos) = stuff(pos) * 3
                mu = False
            End If
        Next
        total = 0
        For pos = 1 To 12 Step 1
            total = total + stuff(pos)
        Next
        check = 10 - (total Mod 10)
    End Sub

    Private Sub results()
        If check = stuff(13) Then
            Console.WriteLine("The ISBN-13 number entered is valid")
        Else
            Console.WriteLine("Invalid ISBN-13 number")
        End If
    End Sub

End Module
Ugh, now I have to present this as a fucking flowchart (I *was* meant to do the flowchart first, but thought "fuck it"). Coding is fun, but flowcharts bore me to tears.
odditude
Official tech support dood
Posts: 2122
Joined: Wed Jan 25, 2006 7:57 am

Post by odditude »

you've got recursion where it has no business ever being.
Why yes, my shift key *IS* broken.
ZH/Franky

Post by ZH/Franky »

odditude wrote:you've got recursion where it has no business ever being.
really? could you explain?
odditude
Official tech support dood
Posts: 2122
Joined: Wed Jan 25, 2006 7:57 am

Post by odditude »

Franky wrote:

Code: Select all

Private Sub validate_isbn13()
    Console.Write("Enter ISBN-13 number: ")
    isbn = Console.ReadLine()
    If isbn.Length <> 13 Then
        Console.WriteLine("Invalid input; ISBN-13 number entered is not 13 digits")
        validate_isbn13()
    End If
...
End Sub
every time someone enters an invalid input, you call the module recursively. there's no repeated calculation, so recursion here is a needless waste of resources (time spent in context switches and space used on the stack, at the minimum).

a better idea would be...

Code: Select all

Private Sub validate_isbn13()
    Console.Write("Enter ISBN-13 number: ")
    isbn = Console.ReadLine()
    while isbn.Length <> 13 Then
        Console.WriteLine("Invalid input; ISBN-13 number entered is not 13 digits")
        Console.Write("Enter ISBN-13 number: ")
        isbn = Console.ReadLine()
    End while
...
End Sub
obviously, the second part would need to be adjusted as well. it would likely be best to rewrite the entire input validation section in a while loop that isn't escaped until the input is valid. you could use an validflag var set by a if/then/else combined with select case validflag to display an error message if necessary, with the while loop also being controlled by validflag.
Why yes, my shift key *IS* broken.
ZH/Franky

Post by ZH/Franky »

odditude wrote:
Franky wrote:

Code: Select all

Private Sub validate_isbn13()
    Console.Write("Enter ISBN-13 number: ")
    isbn = Console.ReadLine()
    If isbn.Length <> 13 Then
        Console.WriteLine("Invalid input; ISBN-13 number entered is not 13 digits")
        validate_isbn13()
    End If
...
End Sub
every time someone enters an invalid input, you call the module recursively. there's no repeated calculation, so recursion here is a needless waste of resources (time spent in context switches and space used on the stack, at the minimum).

a better idea would be...

Code: Select all

Private Sub validate_isbn13()
    Console.Write("Enter ISBN-13 number: ")
    isbn = Console.ReadLine()
    while isbn.Length <> 13 Then
        Console.WriteLine("Invalid input; ISBN-13 number entered is not 13 digits")
        Console.Write("Enter ISBN-13 number: ")
        isbn = Console.ReadLine()
    End while
...
End Sub
obviously, the second part would need to be adjusted as well. it would likely be best to rewrite the entire input validation section in a while loop that isn't escaped until the input is valid. you could use an validflag var set by a if/then/else combined with select case validflag to display an error message if necessary, with the while loop also being controlled by validflag.
Ah, I see. Thanks for the heads up. I've just been using recursion a lot recently because I've found that it often simplifies the code, and my current program works, but I'll try out your idea.
Though, I won't do it now because in 20 minutes I need to be out the door to catch a bus to get to college. Bye.
ZH/Franky

Post by ZH/Franky »

Alright, new code. Did it during my lunchbreak:

Code: Select all

Module Module1

    Private isbn As String
    Private digits(13), total, check As Integer
    Private confirm, proceed As Boolean

    Sub Main()
        Do
            validate_isbn13()
            calculate_check()
            validation_results()
            Console.WriteLine()
        Loop
    End Sub

    Private Sub validate_isbn13()
        confirm = False
        proceed = False
        Do While confirm <> True
            Console.Write("Enter ISBN-13 number (press enter to quit): ")
            isbn = Console.ReadLine()
            If isbn = "" Then
                End
            ElseIf isbn.Length <> 13 Then
                Console.WriteLine("Invalid input; ISBN-13 number must be 13 digits")
            Else
                proceed = True
                For pos = 1 To 13 Step 1
                    Try
                        digits(pos) = Convert.ToInt32(Mid(isbn, pos, 1))
                    Catch ex As Exception
                        proceed = False
                        Exit For
                    End Try
                Next
                If proceed <> False Then
                    confirm = True
                Else
                    Console.WriteLine("Invalid input; some of the characters are not digits")
                End If
            End If
        Loop
    End Sub

    Private Sub calculate_check()
        For pos = 2 To 12 Step 2
            digits(pos) = digits(pos) * 3
            total = total + digits(pos) + digits(pos - 1)
        Next
        check = 10 - (total Mod 10)
        total = 0
    End Sub

    Private Sub validation_results()
        If check <> digits(13) Then
            Console.WriteLine("Invalid ISBN-13 number")
        Else
            Console.WriteLine("The ISBN-13 number entered is valid")
        End If
    End Sub

End Module
I must say, that though it's now much more code in validate_isbn13(), it's much more stable and effecient.
Also cleaned up calculate_check()
odditude
Official tech support dood
Posts: 2122
Joined: Wed Jan 25, 2006 7:57 am

Post by odditude »

Code: Select all

Private Sub validate_isbn13()
    confirm = false
    Do While confirm == false
        Console.Write("Enter ISBN-13 number (press enter to quit): ")
        isbn = Console.ReadLine()
        If isbn.length = 0 Then End
        Else If isbn Like "#############" Then
            confirm = true
        Else
            Console.WriteLine("Invalid input; ISBN-13 number must be 13 digits")
        End If
    Loop
End Sub
more concise, and eliminates the need for processing the string manually (and doing exception handling on that routine). look up the "Like" keyword in VB help.
Why yes, my shift key *IS* broken.
ZH/Franky

Post by ZH/Franky »

odditude wrote:

Code: Select all

Private Sub validate_isbn13()
    confirm = false
    Do While confirm == false
        Console.Write("Enter ISBN-13 number (press enter to quit): ")
        isbn = Console.ReadLine()
        If isbn.length = 0 Then End
        Else If isbn Like "#############" Then
            confirm = true
        Else
            Console.WriteLine("Invalid input; ISBN-13 number must be 13 digits")
        End If
    Loop
End Sub
more concise, and eliminates the need for processing the string manually (and doing exception handling on that routine). look up the "Like" keyword in VB help.
Oh, thanks for the hint. I'll read up on the "like" operator then.
ZH/Franky

Post by ZH/Franky »

Alright, went on google to lookup "vb.net like operator" and now have an understanding of what the "like" operator is for. I went over the code sample you provided, odditude, and I've got to say, my program now looks awesome:

Code: Select all

Module Module1

    Private isbn As String
    Private digits(13), total, check As Integer
    Private proceed As Boolean

    Sub Main()
        Do
            validate_isbn13()
            calculate_check()
            validation_results()
            Console.WriteLine()
        Loop
    End Sub

    Private Sub validate_isbn13()
        proceed = False
        Do While proceed <> True
            Console.Write("Enter ISBN-13 number (press enter to quit): ")
            isbn = Console.ReadLine()
            If isbn.Length = 0 Then
                End
            ElseIf isbn Like "#############" Then
                proceed = True
            Else
                Console.WriteLine("Invalid input; ISBN-13 number must be 13 digits")
            End If
        Loop
    End Sub

    Private Sub calculate_check()
        For pos = 2 To 12 Step 2
            digits(pos - 1) = Convert.ToInt32(Mid(isbn, (pos - 1), 1))
            digits(pos) = Convert.ToInt32(Mid(isbn, pos, 1))
            digits(pos) = digits(pos) * 3
            total = total + digits(pos) + digits(pos - 1)
        Next
        digits(13) = Convert.ToInt32(Mid(isbn, 13, 1))
        check = 10 - (total Mod 10)
        total = 0
    End Sub

    Private Sub validation_results()
        If check <> digits(13) Then
            Console.WriteLine("Invalid ISBN-13 number")
        Else
            Console.WriteLine("The ISBN-13 number entered is valid")
        End If
    End Sub

End Module
Man, I've got to read up about more operators. I mean, the like operator is damn awesome, I'm sure there are other awesome operators that could come in handy at some point.

Again, thanks odditude :)

PS:
If you're interested, here's the page I looked at:
http://getdotnetco.web119.discountasp.n ... erator.htm
odditude
Official tech support dood
Posts: 2122
Joined: Wed Jan 25, 2006 7:57 am

Post by odditude »

have you tested your code? looks like it should die horribly; you're addressing the array incorrectly. arrays are zero-indexed, not one-indexted - foo(13) contains foo(0)...foo(12), not foo(1)...foo(13).
Why yes, my shift key *IS* broken.
ZH/Franky

Post by ZH/Franky »

odditude wrote:have you tested your code? looks like it should die horribly; you're addressing the array incorrectly. arrays are zero-indexed, not one-indexted - foo(13) contains foo(0)...foo(12), not foo(1)...foo(13).
You know, that's actually a good question. I've tested my code many times and it works. Maybe it's such in vb that say you specify something(10), there are actually 11 spaces in the array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); I tested this by specifying "digits(0) = 6" in calculate_check(), and so it would seem that there are actually 14 spaces, not 13, in my array (digits(0) is never actually looked at in my program, btw).
So, my array has digits(0)...digits(13), not digits(0)...digits(12) or digits(1)...digits(13)
Strange, really strange.
Maybe it's just (in vb.net 2008) microsoft trying to be friendly to newcomers that would often make that mistake.

In any case, I fixed the problem:

Code: Select all

Module Module1

    Private isbn As String
    Private digits(13), total, check As Integer
    Private proceed As Boolean

    Sub Main()
        Do
            validate_isbn13()
            calculate_check()
            validation_results()
            Console.WriteLine()
        Loop
    End Sub

    Private Sub validate_isbn13()
        proceed = False
        Do While proceed <> True
            Console.Write("Enter ISBN-13 number (press enter to quit): ")
            isbn = Console.ReadLine()
            If isbn.Length = 0 Then
                End
            ElseIf isbn Like "#############" Then
                proceed = True
            Else
                Console.WriteLine("Invalid input; ISBN-13 number must be 13 digits")
            End If
        Loop
    End Sub

    Private Sub calculate_check()
        For pos = 2 To 12 Step 2
            digits(pos - 2) = Convert.ToInt32(Mid(isbn, (pos - 1), 1))
            digits(pos - 1) = Convert.ToInt32(Mid(isbn, (pos), 1))
            digits(pos - 1) = digits(pos - 1) * 3
            total = total + digits(pos - 1) + digits(pos - 2)
        Next
        digits(12) = Convert.ToInt32(Mid(isbn, 13, 1))
        check = 10 - (total Mod 10)
        total = 0
    End Sub

    Private Sub validation_results()
        If check <> digits(12) Then
            Console.WriteLine("Invalid ISBN-13 number")
        Else
            Console.WriteLine("The ISBN-13 number entered is valid")
        End If
    End Sub

End Module
I'm using vb.net 2008 at home, but at college (in addition to 2008) they have vb.net 2005. I'll test arrays out on there tommorow, see if it's the same thing.

EDIT: Got rid of the need for that "proceed" boolean variable:

Code: Select all

Module Module1

    Private isbn As String
    Private digits(13), pos, total, check As Integer

    Sub Main()
        Do
            validate_isbn13()
            calculate_check()
            validation_results()
            Console.WriteLine()
        Loop
    End Sub

    Private Sub validate_isbn13()
        Do
            Console.Write("Enter ISBN-13 number (press enter to quit): ")
            isbn = Console.ReadLine()
            If isbn.Length = 0 Then
                End
            ElseIf isbn Like "#############" Then
                Exit Do
            Else
                Console.WriteLine("Invalid input; ISBN-13 number must be 13 digits")
            End If
        Loop
    End Sub

    Private Sub calculate_check()
        For pos = 2 To 12 Step 2
            digits(pos - 2) = Convert.ToInt32(Mid(isbn, (pos - 1), 1))
            digits(pos - 1) = Convert.ToInt32(Mid(isbn, (pos), 1))
            digits(pos - 1) = digits(pos - 1) * 3
            total = total + digits(pos - 1) + digits(pos - 2)
        Next
        digits(12) = Convert.ToInt32(Mid(isbn, 13, 1))
        check = 10 - (total Mod 10)
        total = 0
    End Sub

    Private Sub validation_results()
        If check <> digits(12) Then
            Console.WriteLine("Invalid ISBN-13 number")
        Else
            Console.WriteLine("The ISBN-13 number entered is valid")
        End If
    End Sub

End Module
h4tred

Post by h4tred »

From a set point, adding new features or improving on the existing ones increases the complexity. Dealing with that elegantly is where beauty is in ASM.
Quite a good point. I guess I did run off the wagon and started another argument, again. :? I guess you really do know elegant code when you see it:

I must simply be impressed by the silliest and simplest things, to be honest. Like Nach's ZGet. :o
ZH/Franky

Post by ZH/Franky »

Hey, if you're interested for whatever reason, guys, here's the flowchart I've made (click the thumbnail to view it fullscreen):
Image
Funny; I made that flowchart based on the vb.net code that I wrote first. So, I spontaneously decide to try writing another version, literally following the flowchart I made, and here is the (working) result:

Code: Select all

Module Module1

    Private isbn As String
    Private digits(13), pos, total, check As Integer

    Sub Main()
        Do
            Console.Write("ISBN-13: ")
            isbn = Console.ReadLine()
            If isbn.Length = 0 Then
                End
            Else
                If isbn Like "#############" Then
                    Do
                        pos = pos + 2
                        If pos > 12 Then
                            digits(12) = Convert.ToInt32(Mid(isbn, 13, 1))
                            check = 10 - (total Mod 10)
                            total = 0
                            pos = 0
                            If check = digits(12) Then
                                Console.WriteLine("Valid ISBN-13 number")
                            Else
                                Console.WriteLine("Invalid ISBN-13 number")
                            End If
                            Exit Do
                        Else
                            digits(pos - 2) = Convert.ToInt32(Mid(isbn, (pos - 1), 1))
                            digits(pos - 1) = Convert.ToInt32(Mid(isbn, pos, 1))
                            digits(pos - 1) = digits(pos - 1) * 3
                            total = total + digits(pos - 1) + digits(pos - 2)
                        End If
                    Loop
                End If
            End If
        Loop
    End Sub

End Module
...totally different from the original code that I based my flowchart on.
Weird though; though it's all in one messy bulk, it somehow looks more elegant (at least to me, it does).
ZH/Franky

Post by ZH/Franky »

Ok, found a bug in my program. Let me first explain.

I have a copy of the book "Harry Potter and the Prisoner of Azkabam". The ISBN-13 number of this publication is 9780747546290. I entered this number into my isbn13-validator and thought "what the fuck" when it reported that the number is invalid. I first thought I'd discovered that one of JK Rowling's wonderful novels has an invalid ISBN-13 number.

I manually (on paper) calculated the check digit and another "what the fuck" entered my mind. The check digit was 10. Why another "what the fuck"? Because the check digit of an ISBN-13 number is always between 0-9. I read again the section in my textbook that explains the ISBN-13 system, and it turns out that I missed one tiny little detail; a calculated check digit of 10 is written as 0. I added this to my program, and found that upon re-entering the ISBN-13 number of HPATPOA, it was indeed reported as valid by my isbn13-validator.

Here's the new code:

Code: Select all

Module Module1

    Private isbn As String
    Private digits(13), pos, total, check As Integer

    Sub Main()
        Do
            isbn13_validate_isbn13()
            isbn13_calculate_check()
            isbn13_validation_results()
        Loop
    End Sub

    Private Sub isbn13_validate_isbn13()
        Do
            Console.Write("ISBN-13 (press enter to quit): ")
            isbn = Console.ReadLine()
            If isbn.Length = 0 Then
                End
            ElseIf isbn Like "#############" Then
                Exit Do
            Else
                Console.WriteLine("Invalid input")
            End If
        Loop
    End Sub

    Private Sub isbn13_calculate_check()
        For pos = 2 To 12 Step 2
            digits(pos - 2) = Convert.ToInt32(Mid(isbn, (pos - 1), 1))
            digits(pos - 1) = Convert.ToInt32(Mid(isbn, (pos), 1))
            digits(pos - 1) = digits(pos - 1) * 3
            total = total + digits(pos - 1) + digits(pos - 2)
        Next
        digits(12) = Convert.ToInt32(Mid(isbn, 13, 1))
        check = 10 - (total Mod 10)
        If check = 10 Then
            check = 0
        End If
        total = 0
    End Sub

    Private Sub isbn13_validation_results()
        If check <> digits(12) Then
            Console.WriteLine("Invalid ISBN-13 number")
        Else
            Console.WriteLine("Valid ISBN-13 number")
        End If
    End Sub

End Module
Who'd have thought (by sheer luck) that a Harry Potter book would save me from being down-graded when handing in my work eh? I think JK Rowling deserves a hug from yours truly.
But ugh, now I have to draw that fucking flowchart again. AGGGHHHHH!!!
odditude
Official tech support dood
Posts: 2122
Joined: Wed Jan 25, 2006 7:57 am

Post by odditude »

Franky wrote:

Code: Select all

check = 10 - (total Mod 10)
If check = 10 Then
    check = 0
End If
the proper implementation is

Code: Select all

check = (10 - (total Mod 10)) Mod 10
which is what the spec lists.

also, initialize "total" before you use it the first time, not after you use it (for the next time).
Why yes, my shift key *IS* broken.
ZH/Franky

Post by ZH/Franky »

odditude wrote:
Franky wrote:

Code: Select all

check = 10 - (total Mod 10)
If check = 10 Then
    check = 0
End If
the proper implementation is

Code: Select all

check = (10 - (total Mod 10)) Mod 10
which is what the spec lists.

also, initialize "total" before you use it the first time, not after you use it (for the next time).
Ah, that would make sense. It explains the whole "write 10 as zero" thing too. You don't write 10 as 0 just because; it's because of the actual proper calculation that should take place. I really should complain about the AQA specification. A quotation from my textbook:
A 10 is replaced by a 0, so in all cases a single check digit is the result
...it would seem that I need to verify the content's of the official textbook I'm being told to use. Then again, I guess I shouldn't fully trust it anyway. There's obviously more mistakes, like on page 193 where it says "word wide web".

About the "total" initialization thing: I suppose it's good practise to initialize it first, not after. I figured it wouldn't matter because in vb, I've found that when you set an identifier as an integer, double, single or whatever, it is automatically set to 0 by the vb.net compiler. Anyway, I've changed it based on your suggestion.

I'll start paying even more close attention to details from now on.

Anyway, here's the new code, based on your suggestions:

Code: Select all

Module Module1

    Private isbn As String
    Private digits(13), pos, total, check As Integer

    Sub Main()
        Do
            isbn13_validate_isbn13()
            isbn13_calculate_check()
            isbn13_validation_results()
        Loop
    End Sub

    Private Sub isbn13_validate_isbn13()
        Do
            Console.Write("ISBN-13 (press enter to quit): ")
            isbn = Console.ReadLine()
            If isbn.Length = 0 Then
                End
            ElseIf isbn Like "#############" Then
                Exit Do
            Else
                Console.WriteLine("Invalid input")
            End If
        Loop
    End Sub

    Private Sub isbn13_calculate_check()
        total = 0
        For pos = 2 To 12 Step 2
            digits(pos - 2) = Convert.ToInt32(Mid(isbn, (pos - 1), 1))
            digits(pos - 1) = Convert.ToInt32(Mid(isbn, (pos), 1))
            digits(pos - 1) = digits(pos - 1) * 3
            total = total + digits(pos - 1) + digits(pos - 2)
        Next
        digits(12) = Convert.ToInt32(Mid(isbn, 13, 1))
        check = (10 - (total Mod 10)) Mod 10
    End Sub

    Private Sub isbn13_validation_results()
        If check <> digits(12) Then
            Console.WriteLine("Invalid ISBN-13 number")
        Else
            Console.WriteLine("Valid ISBN-13 number")
        End If
    End Sub

End Module
Last edited by ZH/Franky on Thu Nov 20, 2008 11:43 pm, edited 1 time in total.
odditude
Official tech support dood
Posts: 2122
Joined: Wed Jan 25, 2006 7:57 am

Post by odditude »

Franky wrote:I'll start paying even more close attention to details from now on.
The choir of angels wrote:Alleluia!
;)
Franky wrote:I suppose it's good practise to initialize it first, not after.
not all languages initialize variables for you - it's good practice to always do it manually.
Why yes, my shift key *IS* broken.
creaothceann
Seen it all
Posts: 2302
Joined: Mon Jan 03, 2005 5:04 pm
Location: Germany
Contact:

Post by creaothceann »

Franky wrote:Harry Potter and the Prisoner of Azkabam
...

Reminds me of this.
vSNES | Delphi 10 BPLs
bsnes launcher with recent files list
ZH/Franky

Post by ZH/Franky »

Alright, new code. Remember zbinc? My dec2bin, dec2hex, bin2dec, bin2hex, hex2dec, and hex2bin program? Well, that started off as homework (binary to decimal, and decimal to binary). I got set homework today to write a program to do Hexadecimal to Binary, and Hexadecimal to Decimal (for both unsigned and two's complement signed), (and validate all inputs, along with providing a console for the user to decide what s/he wants to do).

This one didn't take me as long because I already had the code in zbinc, so I just cannibalized chunks of code from there and moved bits around, etc. I did, however, rewrite my input validations (using new, better programming techniques that I've learned recently).

Here it is:

Code: Select all

Module Module1

    Private BinaryString, choice, sign, HexString, hexr, digit_string, bits_string As String
    Private digit, bits As Double
    Private proceed, inform_4dv As Boolean

    Sub Main()
        display_options()
        Do
            digit = 0
            BinaryString = ""
            Console.Write("?: ")
            choice = Console.ReadLine()
            Select Case choice
                Case "quit"
                    End
                Case "display"
                    display_options()
                Case "hex2dec"
                    validate_bits()
                    validate_sign()
                    validate_HexString()
                    hex2dec()
                    Console.WriteLine(digit)
                Case "hex2bin"
                    validate_bits()
                    validate_HexString()
                    hex2bin()
                    Console.WriteLine(BinaryString)
                Case ""
                Case Else
                    Console.WriteLine("Invalid option")
            End Select
        Loop
    End Sub

    Private Sub validate_bits()
        Do
            proceed = True
            inform_4dv = False
            Console.Write("Address space: ")
            bits_string = Console.ReadLine()
            Try
                bits = Convert.ToInt32(bits_string)
            Catch ex As Exception
                proceed = False
            End Try
            If proceed = True Then
                If bits < 1 Then
                ElseIf (bits - (Int(bits))) <> 0 Then
                ElseIf ((bits / 4) - Int(bits / 4)) <> 0 Then
                    inform_4dv = True
                Else
                    Exit Do
                End If
            End If
            Console.WriteLine("Invalid input")
            If inform_4dv = True Then
                Console.WriteLine("Note: ensure that 4 is a factor of the address space")
            End If
        Loop
    End Sub

    Private Sub validate_sign()
        Do
            Console.Write("Signed or unsigned: ")
            sign = Console.ReadLine()
            If sign = "signed" Or sign = "unsigned" Then
                Exit Do
            End If
            Console.WriteLine("Invalid input")
        Loop
    End Sub

    Private Sub validate_HexString()
        Do
            proceed = True
            Console.Write("Hex: ")
            HexString = Console.ReadLine()
            If HexString = "" Then
                proceed = False
            Else
                For pos = 1 To HexString.Length Step 1
                    hexr = Mid(HexString, pos, 1)
                    If hexr Like "[a-f]" Or hexr Like "[A-F]" Or hexr Like "#" Then
                    Else
                        proceed = False
                        Exit For
                    End If
                Next
            End If
            If proceed = True Then
                If (HexString.Length * 4) < (bits + 1) Then
                    Exit Do
                End If
            End If
            Console.WriteLine("Invalid input")
        Loop
    End Sub

    Private Sub hex2dec()
        hex2bin()
        For pos = BinaryString.Length To 1 Step -1
            If Mid(BinaryString, pos, 1) = "1" Then
                digit = ((2 ^ (BinaryString.Length - pos)) + digit)
            End If
        Next
        If sign = "signed" And BinaryString.Length = bits And (Mid(BinaryString, 1, 1)) = "1" Then
            digit = (digit - (2 ^ bits))
        End If
    End Sub

    Private Sub hex2bin()
        For pos = 1 To HexString.Length Step 1
            Select Case Mid(HexString, pos, 1)
                Case "0"
                    BinaryString = BinaryString + "0000"
                Case "1"
                    BinaryString = BinaryString + "0001"
                Case "2"
                    BinaryString = BinaryString + "0010"
                Case "3"
                    BinaryString = BinaryString + "0011"
                Case "4"
                    BinaryString = BinaryString + "0100"
                Case "5"
                    BinaryString = BinaryString + "0101"
                Case "6"
                    BinaryString = BinaryString + "0110"
                Case "7"
                    BinaryString = BinaryString + "0111"
                Case "8"
                    BinaryString = BinaryString + "1000"
                Case "9"
                    BinaryString = BinaryString + "1001"
                Case "A"
                    BinaryString = BinaryString + "1010"
                Case "a"
                    BinaryString = BinaryString + "1010"
                Case "B"
                    BinaryString = BinaryString + "1011"
                Case "b"
                    BinaryString = BinaryString + "1011"
                Case "C"
                    BinaryString = BinaryString + "1100"
                Case "c"
                    BinaryString = BinaryString + "1100"
                Case "D"
                    BinaryString = BinaryString + "1101"
                Case "d"
                    BinaryString = BinaryString + "1101"
                Case "E"
                    BinaryString = BinaryString + "1110"
                Case "e"
                    BinaryString = BinaryString + "1110"
                Case "F"
                    BinaryString = BinaryString + "1111"
                Case "f"
                    BinaryString = BinaryString + "1111"
            End Select
        Next
    End Sub

    Private Sub display_options()
        Console.WriteLine("hex2dec - Hexadecimal to decimal conversion")
        Console.WriteLine("hex2bin - Hexadecimal to binary conversion")
        Console.WriteLine("display - display options")
        Console.WriteLine("quit - exit this program")
    End Sub

End Module
Guess what? This time I need to draw an even bigger flowchart. Ah, the joys.
MisterJones
Trooper
Posts: 387
Joined: Fri Jul 30, 2004 6:25 am
Location: Mexico
Contact:

Post by MisterJones »

I have (or used to have) a charting app that we got for our algorithm design class, that I can spare to you if you want (assuming that thing still exists).
_-|-_
creaothceann
Seen it all
Posts: 2302
Joined: Mon Jan 03, 2005 5:04 pm
Location: Germany
Contact:

Post by creaothceann »

Franky wrote:

Code: Select all

    Private Sub hex2bin()
        For pos = 1 To HexString.Length Step 1
            Select Case Mid(HexString, pos, 1)
                Case "0"
                    BinaryString = BinaryString + "0000"
                Case "1"
                    BinaryString = BinaryString + "0001"
                Case "2"
                    BinaryString = BinaryString + "0010"
                Case "3"
                    BinaryString = BinaryString + "0011"
                Case "4"
                    BinaryString = BinaryString + "0100"
                Case "5"
                    BinaryString = BinaryString + "0101"
                Case "6"
                    BinaryString = BinaryString + "0110"
                Case "7"
                    BinaryString = BinaryString + "0111"
                Case "8"
                    BinaryString = BinaryString + "1000"
                Case "9"
                    BinaryString = BinaryString + "1001"
                Case "A"
                    BinaryString = BinaryString + "1010"
                Case "a"
                    BinaryString = BinaryString + "1010"
                Case "B"
                    BinaryString = BinaryString + "1011"
                Case "b"
                    BinaryString = BinaryString + "1011"
                Case "C"
                    BinaryString = BinaryString + "1100"
                Case "c"
                    BinaryString = BinaryString + "1100"
                Case "D"
                    BinaryString = BinaryString + "1101"
                Case "d"
                    BinaryString = BinaryString + "1101"
                Case "E"
                    BinaryString = BinaryString + "1110"
                Case "e"
                    BinaryString = BinaryString + "1110"
                Case "F"
                    BinaryString = BinaryString + "1111"
                Case "f"
                    BinaryString = BinaryString + "1111"
            End Select
        Next
    End Sub
Converting the string to uppercase will make the sub smaller.

And consider this:

Code: Select all

BinaryString := '';
for i := 1 to Length(HexString) do begin
        c := UpCase(HexString[i]);
        if (c <  '0') then {error}                              else
        if (c <= '9') then tmp := Ord(c) - Ord('0')             else
        if (c <= 'F') then tmp := Ord(c) - Ord('A') + 10        else {error}
        {use tmp to add binary digits to BinaryString}
end;
(Pascal code)
vSNES | Delphi 10 BPLs
bsnes launcher with recent files list
ZH/Franky

Post by ZH/Franky »

MisterJones wrote:I have (or used to have) a charting app that we got for our algorithm design class, that I can spare to you if you want (assuming that thing still exists).
Ah, thanks :) I can imagine that something like that may come in handy. Could I have a copy?
creaothceann wrote: Converting the string to uppercase will make the sub smaller.

And consider this:

Code: Select all

BinaryString := '';
for i := 1 to Length(HexString) do begin
        c := UpCase(HexString[i]);
        if (c <  '0') then {error}                              else
        if (c <= '9') then tmp := Ord(c) - Ord('0')             else
        if (c <= 'F') then tmp := Ord(c) - Ord('A') + 10        else {error}
        {use tmp to add binary digits to BinaryString}
end;
(Pascal code)
Yeah, I thought that, when writing that select construct, there might be a simpler way. How do I convert lowercase to uppercase?
Also, I don't quite understand that pascal program (never even touched pascal). Could you explain how it works?
Post Reply