|
Download header file - 1 Kb
Contents:
Shortly after VC6 came out, I was rebuilding a program into separate DLL modules, and noticed that
the sum of the sizes of these release built DLLs were a lot higher than I
expected - almost an additional two megabytes larger than the original single
EXE. So I went back and checked the output size of the EXE from what Visual
C++ 5.0 was producing versions what 6.0 was producing - the 6.0 version was much
larger, even with all the project optimizations turned on.
As it turns out, both VC5 and VC6 default to a code generation method that
does not produce the smallest code possible, even when you have that option
turned on in the project settings. In addition, VC6 and VC7 use a different padding
value that VC5 - 4k vs. 512 bytes. This causes each section (.data, etc) to be
rounded up to the next 4k boundary, which causes excessive file bloat.
The reason for the 4k rounding in VC6 seems to be because of the Win98 file
tuning tool - it likes programs that are on the 4k boundary, since they fit
nicely in the x86 virtual memory page. More on why this is not
a stellar idea later.
Rather than messing around with a bunch of project settings, I've provided a
nice and simple header file that you can include in your Stdafx.h (or anywhere -
this is not MFC-only stuff). It works for both VC5 and VC6, it works
marginally for VC7 (.NET), and may work
for VC4 - haven't tried it there. It only kicks in during Release builds, so
it's safe to leave in for debug builds, too.
The header tells the compiler to use certain optimization settings that
removes frame pointers in the source code, which saves space and time. It then
has the linker merge the .data (your text strings, constants, tables, etc),
.text (where you code is), the .rdata (re-only data - consts and etc - see note),
and the .reloc (relocation data) into a single unit . This cuts down on space
taken by the rounding-up of these areas, and is especially noticeable with small
dlls and CPL programs. The final twist is to tell VC6 to behave like VC5 and use
512 byte padding instead of 4k, which further shrinks the output. The header
file is pretty well commented, and can be dropped into almost any program. I've
used this in both small and large applications, and it definitely helps with the
output. For example, I got a 93k exe down to 52k with this, and a control panel
applet down to 4k from 33k (see RRLoginV3
for an example of this savings). Larger programs do not seem (from a filesize
standpoint) to benefit, since the space saved is smaller compared to the larger
file (40k from a 1600k exe doesn't impress many people). Loading times are
faster, however.
When using this header, please be sure to fully test your release builds
before shipping them. Heck, test them without the header too just to prove
that it's not causing the problem (which I doubt that it is - four years,
fifteen major projects I used it with, at least 21,000 other people using
it - no problems). Some code that works fine under Debug will break under
Release - this is due to Microsoft's optimizations, and is a know problem with
all optimizers on all platforms, and coding in general - amazing how many time
we don't check for null pointers or bad data before we use something. Debug will
zero things out for us, set them to know values (0xCC etc), where as Release
it's random. Generally, if you are getting continual GPF's
when using this header, either stop using or else track down the line in your
code that is causing it.
There are some tradeoffs, of course. Linking is slower due to the merging of
the data segments into one, and in general, compressing the file will not be any
better than before (this is because the empty space is removed before
compression, where as before, it was hidden by compression). For me, these are reasonable tradeoffs, since I do not make release
builds every day, and any space saved in the exe generally means a smaller download for my
users, and definitely less memory and space used on the user's system!
Merging the .rdata with static MFC will almost always result in a larger EXE.
This also seems to affect things when you are mixing static libs (either
3rd-party or your own in-house stuff) with MFC, linking it static or not. If you
really want to merge the .rdata section with the rest, define _MERGE_RDATA_
in your project or before including AggressiveOptimize.h header.
Why Not
The argument can be made that doing this is a waste of time, since the
"zero bytes" will be compressed out in a zip file or install archive.
Not really - it doesn't matter if the data is a string of zeroes or ones or
85858585 - it will still take room (20 bytes in a zip file, 29 bytes if only *4*
of them 4k bytes are not the same) and time to compress that data and decompress
it. Also, 20k of zeros is NOT 20k on disk - it's the size of the cluster slop-
for Fat32 systems, 20k can be 32k, NTFS could make it 24k if you're just 1 byte
over (round up). Most end users do not have the dual P4 Xeon systems with two
gigs of RDram and a Raid 0+1 of Western Digital 120meg Special Editions that all
worthy developers have (all six of us), so they will need any space and LOADING
TIME savings they will need; taking an extra 32k or more out of your end user's
64megs of ram on Windows 98 is Not a Good Thing.
.NET
With Visual Studio .NET, aka, VC7, this header does not help as much as it
does under VC6 - this is because Microsoft is not allowing the compiler options
to be set from within the source code like before. You will have to add in the
switches yourself - "/ignore:4078 /RELEASE /LTCG:NOSTATUS" in
the Linker Command Line options, and "/GL /opt:nowin98" in the
C++ Command Line Options. You should also add in "/GA" into your .EXE
project's C++ Command Line Options, too. These will turn off the merging
warnings, force release builds on, and perform whole code optimizations, in
addition to removing the padding. The /GA option will turn on Windows
Application optimization - only do this for EXE's, not DLL's.
VC7 simply does not produce as tight as code as VC6 and VC5 does. The exact
same code is padded out more, and uses bigger constructs. Benchmarking is hard
on the Uber Box, so while this VC7 code might run faster, it's hard to prove it
- perhaps on a slower machine where the runtimes would be more spread out.
However, you generally want smaller code, in an effort to put as much into the
CPU's cache as possible - keep it from hitting slower ram.
Disassembling
Interestingly, and I feel, an added bonus, once the .TEXT segments have been
merged with the .DATA segments, the code will not longer be able to be
disassembled by DUMPBIN (try it! DUMPBIN /disasm filename.exe) or WinDisasm or
any other disassembly tool; you can still hack at it with SoftICE and the like,
of course. Credit must be given to Gëzim Pani <gpani@siu.edu>
for discovering this and asking "why?".
Now, as to an explanation - as I see it. Code is supposed, by historic default,
live within the .TEXT segment (why not .CODE? Good question). The /merge:.text=.data
line causes the .TEXT and .DATA segments to be merged into .DATA, like this (summarized):
| Application (.EXE) |
Dynamic Link Lib (.DLL) |
C:>DUMPBIN Crc32.exe
Dump of file Crc32.exe
File Type: EXECUTABLE IMAGE
Summary
2000 .data
1000 .rdata
1000 .rsrc
|
C:>DUMPBIN CBase.dll
Dump of file CBase.dll
File Type: DLL
Summary
4000 .data
4000 .rdata
1000 .reloc
1000 .rsrc
|
Neither of these files will disassemble, since there is no .text segments. Using
DUMPBIN /header on the CRC32.exe file shows two important items: first, the
entry point is at 38DF RVA (rva is an address into the relocation virtual
address). This is squarely in the 2nd segment, .DATA, which starts at 2000
virtual address, and is 1CE8 long. What this means is that if you have some
program that is hard-coded to expect raw code to be living in the .TEXT segment
(such as the dissemblers!), then it's going to fail. As a test, I tried Shrinker from Blink-Inc (this is a runtime program compressor, that puts
your dlls/exes in a runtime wrapper to compress/decompress in memory
transparently to the program & user; for a freeware open source version, try
UPX), and it worked fine. So
unless something is hacking around with the .TEXT segment explicitly, then there
is no problems. Since the only thing that I know of that does this is code
modification tools and viruses, the question arises - does this provide any sort
of anti-virus protection? Or does it in do the opposite, make it worse, by
causing the virus scanner to fail? Oddly enough, neither Symantec nor McAfee
would deem us worthy to answer our email - McAfee went as far as to demand that
we subscribe to their anti-virus service first! I personally think that this
will not cause a problem with the scanners, since they are looking
for signatures - patterns within the files, which would be independent of how
the program was put together. False positives always happen. However, this might
not be true for the virii - if they patch the first .TEXT segment, they will
fail. But if they walk the header and patch the entry point (which is how I
think they operate, but who knows these days), then this will matter not to
them. But since this is pure speculation (but with 20-some odd years of writing
code to back it up), until and if we get a response from some anti-virus vendor,
that is just a guess.
This article and source code are copyrighted © 1999-2002 by Todd
C. Wilson (tcw@nopcode.com). No reproduction of this article may be made
without proper clearance from the author. Free use of the source as described in
the source files is allowed, but may not be claimed as your own work. You may not
re-publish this article nor the attached files on any other web site or medium
without prior permission; you may refer to NOPcode.com
as to where to get it. You may, of course, use this in your own projects.
If you are using this in your projects or example code and would like to let the
world know, drop us a line!
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 64 (Total in Forum: 64) (Refresh) | FirstPrevNext |
|
 |
|
|
Great article. Greetings!
I'd like to know if someone has done any performance tests compiling such optimizations with Visual Studio C++ 2005/2008 and Windows XPsp2 or Windows Vista as target Operting System. Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I can't actually verify this because I don't have an Athlon64 or a Pentium with NX protection, but the following line will cause problems:
#pragma comment(linker,"/merge:.text=.data")
If I'm reading this right, it's telling the linker "code is data" which is exactly what XP SP2 doesn't allow (NX = "no execute" of data).
-Oz
|
| Sign In·View Thread·PermaLink | 4.60/5 (4 votes) |
|
|
|
 |
|
|
Hey Oz!
I wish I'd found this message before spending the last 3 hours trying to work out why some of my COM DLLs were failing to register!
I think you're definitely right - I'm running XP Professional SP2 on an Athlon64 system, and DEP won't allow regsvr32 to register DLLs that were built with this optimisation.
Cheers,
Gary
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
This is what some hacker wrote back about my software that I used this in:
me: If your 30 day trial runs out and you want to test a newer version, let me know and I'll send you a temporary license code.
HACK: Ok, though it would be easier just to crack it. 
HACK: Out of curiosity, your application has some "flaws" which make running a disassembler difficult (haven't really bothered trying too hard; dead-listing is not my style). Was that delibrate, or just a side effect of whatever compiler you use?
me: snicker snicker
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
To get a small exe in .NET add "/filealign:16 /align:16" to the linker command line in your release project.
[]'s Richard Natal
--< Rick-SP >-- Join to the best... Join to C++ developers
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I tried to combine Pietrek's code with AggressiveOptimize.h
http://msdn.microsoft.com/msdnmag/issues/01/01/hood/
I inserted the
#include "AggressiveOptimize.h"
as the first to each cpp, and then:
nmake -f libctiny.mak cl hello.cpp libctiny.lib del *.obj
The result -- EXE is the same (2.5 K), I can even see a .TEXT section with DUMPBIN.
Dump of file hello.exe Summary 1000 .data 1000 .rdata 1000 .text
What am I doing wrong?
Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Works great, I wasn't concerned with size but rather with execution speed. My software performs several computational intensive tasks, mainly image processing algorithms. I managed to shave off 30ms of execution time which brings it close to a real-time behaviour, and this is a big improvement for me. Thanks 
Marius
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Generally, if this is happening, then the algorithm you're using is packing-sensitive - in other words, the instructions in the loop are all fitting within the CPU's cache, and a change in a totally unrelated section of code might cause the address of this bit of code to shift, making it NOT pack better. Not to mention that a different CPU stepping might cause slightly different results.
You might want to spend the time profiling your code and hand-tuning it, and even consider manipulating some of it in raw assembly. Intel offers their VTune product as a 30-day eval, and while I'm not thrilled with what they tout it for, the only feature I found useful was a line-by-line display of execution time, so you can see how fast specific lines of code run and where to massage it at.
"I was in a computer game. Funny as hell, it was the most horrible thing I could think of."
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Works great, I wasn't concerned with size but rather with execution speed. My software performs serveral computational intensive tasks, mainly image processing algorithms. I managed to shave of 30ms of execution time which brings it close to a real-time behaviour, and this is a big improvement for me. Thanks 
Marius
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Very easy to impliment and has resulted in substantial gains for my project. I have many DLLs that were averaging 20KB-72KB (average having already had some .exe reducing tips applied to them). The larger DLLs (where there was actually a substancial amount of code and API calls) were reduced by about 20% in size, and some of the samller ones (resource libraries and such) to 3 or 4KB! This is exactly what I have been searching for, very good work!
I haven't done any testing to see if any of this reduces RAM use, but a quick glance at the mem usuage output suggests some small savings there too.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I tested your header with cximage[^], the result is -10% in disk space and -15% in memory. It's a good result, but far from the 25% of your reports. 5 stars anyway
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Often overlooked - For more info on the subject, check out the 'Under the hood' articel by Matt Pietrek (http://msdn.microsoft.com/msdnmag/issues/01/01/hood/default.aspx)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
This is from a REAL WORLD project that is SHIPPING to our customers. It's a large project with lots of database usage, that runs within a mixed 95/98/me/xp/2k/xyz setting. This is from an excel spreadsheet; the filenames have been changed for NDA reasons. These results are 100% reproducable - please, if you are confused about if this works for you or not, first do your own benchmarks and then post them! Hey, let's have a compitition - who can get the most savings? NOTE: dunno how well this is going to paste in here..... I can email the orignal XLS file if anyone wants it...
Optmized with header: Without header: Savings % smaller Size (Bytes) Filename Size (Bytes) Filename "199,168" AdminDB.dll "262,144" AdminDB.dll "62,976" 24.02% "185,344" AdminGUI.dll "221,184" AdminGUI.dll "35,840" 16.20% "114,176" BillingGUI.dll "151,552" BillingGUI.dll "37,376" 24.66% "62,976" CalendarDB.dll "86,016" CalendarDB.dll "23,040" 26.79% "137,216" CalendarGUI.dll "167,936" CalendarGUI.dll "30,720" 18.29% "138,240" ConditionalsDB.dll "176,128" ConditionalsDB.dll "37,888" 21.51% "198,144" ConditionalsGUI.dll "241,664" ConditionalsGUI.dll "43,520" 18.01% "111,616" ContactsGUI.dll "143,360" ContactsGUI.dll "31,744" 22.14% "60,928" EMailDB.dll "86,016" EMailDB.dll "25,088" 29.17% "117,248" EMailGUI.dll "151,552" EMailGUI.dll "34,304" 22.64% "88,576" CoreBase.dll "122,880" CoreBase.dll "34,304" 27.92% "328,192" CoreControls.dll "401,408" CoreControls.dll "73,216" 18.24% "128,000" CoreDBMgr.dll "184,320" CoreDBMgr.dll "56,320" 30.56% "110,592" CoreDialogs.dll "135,168" CoreDialogs.dll "24,576" 18.18% "307,712" CoreToolkit.dll "389,120" CoreToolkit.dll "81,408" 20.92% "110,592" Client.exe "126,976" Client.exe "16,384" 12.90% "431,104" ClientComponents.dll "561,152" ClientComponents.dll "130,048" 23.18% "14,336" ClientDBMgr.dll "28,672" ClientDBMgr.dll "14,336" 50.00% "367,616" ClientDialogs.dll "454,656" ClientDialogs.dll "87,040" 19.14% "22,016" ClientToolkit.dll "36,864" ClientToolkit.dll "14,848" 40.28% "230,400" HoldingsDB.dll "290,816" HoldingsDB.dll "60,416" 20.77% "511,488" HoldingsGUI.dll "651,264" HoldingsGUI.dll "139,776" 21.46% "77,312" FolderListGUI.dll "94,208" FolderListGUI.dll "16,896" 17.93% "282,112" OrdersDB.dll "364,544" OrdersDB.dll "82,432" 22.61% "120,832" OrdersGUI.dll "147,456" OrdersGUI.dll "26,624" 18.06% "23,040" SystemNotificationsDB.dll "36,864" SystemNotificationsDB.dll "13,824" 37.50% "85,504" TaskDB.dll "114,688" TaskDB.dll "29,184" 25.45% "71,168" TaskGUI.dll "94,208" TaskGUI.dll "23,040" 24.46% "6,144" TitleDB.dll "24,576" TitleDB.dll "18,432" 75.00% "49,152" TitleGUI.dll "69,632" TitleGUI.dll "20,480" 29.41% "229,376" Services.exe "229,376" Services.exe 0 0.00% "20,480" ServicesNTLoader.exe "20,480" ServicesNTLoader.exe 0 0.00% "77,824" PDFout.dll "77,824" PDFout.dll 0 0.00% "5,018,624 bytes in 33 files and 2 dirs" "6,344,704 bytes in 33 files and 2 dirs" 1326080 25.91% "5,079,040 bytes allocated" "6,352,896 bytes allocated" Average savings Slop: "60,416" (118 512-byte clusters) "8,192" (16 512-byte clusters)
Visual Studio Favorites - improve your development! GUIgui - skin your apps without XP
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Almost forgot. Compressed sizes, using WinZip, Maximum compression: Optmized.zip - 1.73 MB (1,822,928 bytes) on disk: 1.74 MB (1,826,816 bytes) Unoptimized.zip - 2.01 MB (2,110,334 bytes) on disk: 2.01 MB (2,113,536 bytes)
Visual Studio Favorites - improve your development! GUIgui - skin your apps without XP
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
OK, that's what I say very specific cases. Most of you components are about 40-50 K and surely this will help in this case. Please read my reponse to Tim Kosse in the thread below.
And, please, please do not take this as an offense. I may be rude in the beginning but I apologize for that. I just wanted to write my opinions. I did not mean to insult, or ..etc. And please continue writing more articles. This kind of comments should not dicourage you. This tip may not be helpful for me; but it may be very useful for somebody else.
Mustafa Demirhan http://www.macroangel.com Sonork ID 100.9935:zoltrix
They say I'm lazy but it takes all my time
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
If it did not help for you, perhaps you could post your results. It would be interesting to see more metrics than just the projects me and the others are using with it.
Visual Studio Favorites - improve your development! GUIgui - skin your apps without XP
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Todd C. Wilson wrote: If it did not help for you, perhaps you could post your results. It would be interesting to see more metrics than just the projects me and the others are using with it.
Well, you did not get my point: I did not say that it does not help. It is just a very very very small reduction in the size. Nothing else!!!
But just FYI, here are the results for one of my projects:
Original .exe Size (without using the library): 4,112,483 bytes Size after optimization: 4,104,291 bytes Gain = 8,192 Bytes Gain in percentage = 0.199%
Apart from the exe file, the project has 7 DLL files. All of the files have similar gains. So totally:
The total size of the project before optimization: 7,626,752 bytes The total size after optimization: 7,553,024 bytes Total Gain = 73,728 bytes Total Gain in percentage: 0.96%
As you see, in a real project the gain will be less than 1%. Also, this will be the case for most of the applications not your example. So tell me: Who cares about this 1% reduction in size?
Mustafa Demirhan http://www.macroangel.com Sonork ID 100.9935:zoltrix
They say I'm lazy but it takes all my time
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Mustafa Demirhan wrote: As you see, in a real project the gain will be less than 1%. Also, this will be the case for most of the applications not your example. So tell me: Who cares about this 1% reduction in size?
I would suspect that you have already changed a lot of your compiler settings for this project, so what the header is doing, you've already done. If you read the article and the header, you'll notice that I mention that instead of farting around with the settings for each project, the header does it for you. If you wish to post your project's C++ options settings, we can see what you've done already do it. You might want to try resetting your project back to the default and compile it again without this header and see what it does.
As for this being a real project or not, I have no idea what your project is or what it is doing. However, the projects I usually work on are corporate enterprise deliverables, and are much larger than a single exe and seven dlls. FYI, the real-world project that I gave as an example is used world wide by a Well Known Corporation, which is why I had to change the names to protect the NDA.
Also, as mentioned, your milage may vary - nobody is forcing you to use this thing, but a lot of people have, and it has always helped. You seem to be more pissed off about the fact that it doesn't make your program super-spiffy, and only gives you 73k of savings - this I would chalk up to more of your OWN optimization settings more than anything else. You're trying to place "blame" for something where you've already done most of the same work!
Visual Studio Favorites - improve your development! GUIgui - skin your apps without XP
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Here is another benchmark:
File Size Before Optimization: 2,174,976 bytes File Size After Optimization: 2,039,296 bytes Gain: 135,680 bytes Percentage in gain: 6%
In this project it has a much bigger gain; but again the gain in the file size is not important. But I did a test for memory usage and you were right. The optimized program uses less memory. Now, this I call gain I do not care about the HDD space but 100 KB of gain in the memory for each component is a quite good improvement. Sorry for being very harh before. Thanks for the code
Mustafa Demirhan http://www.macroangel.com Sonork ID 100.9935:zoltrix
They say I'm lazy but it takes all my time
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Mustafa Demirhan wrote:
One more thing: I think you also used MiniCRT. Right? If so, this picture is totally useless. Especially the compressed sizes will be nearly the same...
Nope. Standard MFC42.dll MSVCRT.dll etc. The compressed sizes does not include the same 3rd party dlls, only the ones that are generated by the project and the build tools. MiniCRT is only of value if you are totally forgoing the *entire* Microsoft libaries, and this includes MFC, ATL, MSVCRT, etc, not to mention global objects. I wouldn't consider using MiniCRT or any similar libary (there are dozens) for anything bigger than a single console app.
Visual Studio Favorites - improve your development! GUIgui - skin your apps without XP
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
From the article: Why Not The argument can be made that doing this is a waste of time, since the "zero bytes" will be compressed out in a zip file or install archive. Not really - it doesn't matter if the data is a string of zeroes or ones or 85858585 - it will still take room (20 bytes in a zip file, 29 bytes if only *4* of them 4k bytes are not the same) and time to compress that data and decompress it. Also, 20k of zeros is NOT 20k on disk - it's the size of the cluster slop- for Fat32 systems, 20k can be 32k, NTFS could make it 24k if you're just 1 byte over (round up). Most end users do not have the dual P4 Xeon systems with two gigs of RDram and a Raid 0+1 of Western Digital 120meg Special Editions that all worthy developers have (all six of us), so they will need any space and LOADING TIME savings they will need; taking an extra 32k or more out of your end user's 64megs of ram on Windows 98 is Not a Good Thing.
Just a lie. Totally ridiculous/wrong. Just trying to confuse people!
Mustafa Demirhan http://www.macroangel.com Sonork ID 100.9935:zoltrix
They say I'm lazy but it takes all my time
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Mustafa Demirhan wrote: Just a lie. Totally ridiculous/wrong. Just trying to confuse people!
I must agree - your explanation makes so much more sense than the authors. Oh, wait a minute.....
In other words, what the hell is your problem ? It's unlikely the author set out to lie/confuse, so if you know better, then everyone, the author included, would be interested to hear why. Without that explanation, all you're doing is blowing hot air...
Christian
No offense, but I don't really want to encourage the creation of another VB developer. - Larry Antram 22 Oct 2002
Hey, at least Logo had, at it's inception, a mechanical turtle. VB has always lacked even that... - Shog9 04-09-2002
During last 10 years, with invention of VB and similar programming environments, every ill-educated moron became able to develop software. - Alex E. - 12-Sept-2002
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
By the way, I agree that I was rude. I really did not mean that. I just wanted to point out that these are wrong, but I was way too rude. I am rwally sorry for that. I apologize for being rude.
Mustafa Demirhan http://www.macroangel.com Sonork ID 100.9935:zoltrix
They say I'm lazy but it takes all my time
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|