/[pcsx2_0.9.7]/trunk/pcsx2/System.cpp
ViewVC logotype

Contents of /trunk/pcsx2/System.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 280 - (show annotations) (download)
Thu Dec 23 12:02:12 2010 UTC (9 years ago) by william
File size: 18944 byte(s)
re-commit (had local access denied errors when committing)
1 /* PCSX2 - PS2 Emulator for PCs
2 * Copyright (C) 2002-2010 PCSX2 Dev Team
3 *
4 * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5 * of the GNU Lesser General Public License as published by the Free Software Found-
6 * ation, either version 3 of the License, or (at your option) any later version.
7 *
8 * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10 * PURPOSE. See the GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along with PCSX2.
13 * If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include "PrecompiledHeader.h"
17 #include "Common.h"
18 #include "IopCommon.h"
19 #include "VUmicro.h"
20 #include "newVif.h"
21
22 #include "SamplProf.h"
23
24 #include "Elfheader.h"
25
26 #include "System/RecTypes.h"
27
28 #include "Utilities/MemsetFast.inl"
29
30
31 // --------------------------------------------------------------------------------------
32 // RecompiledCodeReserve (implementations)
33 // --------------------------------------------------------------------------------------
34
35 // Constructor!
36 // Parameters:
37 // name - a nice long name that accurately describes the contents of this reserve.
38 RecompiledCodeReserve::RecompiledCodeReserve( const wxString& name, uint defCommit )
39 : BaseVmReserveListener( name )
40 {
41 m_blocksize = (1024 * 128) / __pagesize;
42 m_prot_mode = PageAccess_Any();
43 m_def_commit = defCommit / __pagesize;
44
45 m_profiler_registered = false;
46 }
47
48 RecompiledCodeReserve::~RecompiledCodeReserve() throw()
49 {
50 _termProfiler();
51 }
52
53 void RecompiledCodeReserve::_registerProfiler()
54 {
55 if (m_profiler_name.IsEmpty() || !IsOk()) return;
56 ProfilerRegisterSource( m_profiler_name, m_baseptr, GetReserveSizeInBytes() );
57 m_profiler_registered = true;
58 }
59
60 void RecompiledCodeReserve::_termProfiler()
61 {
62 if (m_profiler_registered)
63 ProfilerTerminateSource( m_profiler_name );
64 }
65
66 uint RecompiledCodeReserve::_calcDefaultCommitInBlocks() const
67 {
68 return (m_def_commit + m_blocksize - 1) / m_blocksize;
69 }
70
71 void* RecompiledCodeReserve::Reserve( size_t size, uptr base, uptr upper_bounds )
72 {
73 if (!_parent::Reserve(size, base, upper_bounds)) return NULL;
74 _registerProfiler();
75 return m_baseptr;
76 }
77
78
79 // Sets the abbreviated name used by the profiler. Name should be under 10 characters long.
80 // After a name has been set, a profiler source will be automatically registered and cleared
81 // in accordance with changes in the reserve area.
82 RecompiledCodeReserve& RecompiledCodeReserve::SetProfilerName( const wxString& shortname )
83 {
84 m_profiler_name = shortname;
85 _registerProfiler();
86 return *this;
87 }
88
89 void RecompiledCodeReserve::DoCommitAndProtect( uptr page )
90 {
91 CommitBlocks(page, (m_pages_commited || !m_def_commit) ? 1 : _calcDefaultCommitInBlocks() );
92 }
93
94 void RecompiledCodeReserve::OnCommittedBlock( void* block )
95 {
96 if (IsDevBuild)
97 {
98 // Clear the recompiled code block to 0xcc (INT3) -- this helps disasm tools show
99 // the assembly dump more cleanly. We don't clear the block on Release builds since
100 // it can add a noticeable amount of overhead to large block recompilations.
101
102 memset_sse_a<0xcc>( block, m_blocksize * __pagesize );
103 }
104 }
105
106 // This error message is shared by R5900, R3000, and microVU recompilers. It is not used by the
107 // SuperVU recompiler, since it has its own customized message.
108 void RecompiledCodeReserve::ThrowIfNotOk() const
109 {
110 if (IsOk()) return;
111
112 throw Exception::OutOfMemory(m_name)
113 .SetDiagMsg(pxsFmt( L"Recompiled code cache could not be mapped." ))
114 .SetUserMsg( pxE( "!Notice:Recompiler:VirtualMemoryAlloc",
115 L"This recompiler was unable to reserve contiguous memory required for internal caches. "
116 L"This error can be caused by low virtual memory resources, such as a small or disabled swapfile, "
117 L"or by another program that is hogging a lot of memory. You can also try reducing the default "
118 L"cache sizes for all PCSX2 recompilers, found under Host Settings."
119 ));
120 }
121
122
123 void SysOutOfMemory_EmergencyResponse(uptr blocksize)
124 {
125 // An out of memory error occurred. All we can try to do in response is reset the various
126 // recompiler caches (which can sometimes total over 120megs, so it can be quite helpful).
127 // If the user is using interpreters, or if the memory allocation failure was on a very small
128 // allocation, then this code could fail; but that's fine. We're already trying harder than
129 // 99.995% of all programs ever written. -- air
130
131 if (Cpu)
132 {
133 Cpu->SetCacheReserve( (Cpu->GetCacheReserve() * 2) / 3 );
134 Cpu->Reset();
135 }
136
137 if (CpuVU0)
138 {
139 CpuVU0->SetCacheReserve( (CpuVU0->GetCacheReserve() * 2) / 3 );
140 CpuVU0->Reset();
141 }
142
143 if (CpuVU1)
144 {
145 CpuVU1->SetCacheReserve( (CpuVU1->GetCacheReserve() * 2) / 3 );
146 CpuVU1->Reset();
147 }
148
149 if (psxCpu)
150 {
151 psxCpu->SetCacheReserve( (psxCpu->GetCacheReserve() * 2) / 3 );
152 psxCpu->Reset();
153 }
154 }
155
156
157 #if _MSC_VER
158 # include "svnrev.h"
159 #endif
160
161 const Pcsx2Config EmuConfig;
162
163 // Provides an accessor for quick modification of GS options. All GS options are allowed to be
164 // changed "on the fly" by the *main/gui thread only*.
165 Pcsx2Config::GSOptions& SetGSConfig()
166 {
167 //DbgCon.WriteLn( "Direct modification of EmuConfig.GS detected" );
168 AffinityAssert_AllowFrom_MainUI();
169 return const_cast<Pcsx2Config::GSOptions&>(EmuConfig.GS);
170 }
171
172 // Provides an accessor for quick modification of Recompiler options.
173 // Used by loadGameSettings() to set clamp modes via database at game startup.
174 Pcsx2Config::RecompilerOptions& SetRecompilerConfig()
175 {
176 //DbgCon.WriteLn( "Direct modification of EmuConfig.Gamefixes detected" );
177 AffinityAssert_AllowFrom_MainUI();
178 return const_cast<Pcsx2Config::RecompilerOptions&>(EmuConfig.Cpu.Recompiler);
179 }
180
181 // Provides an accessor for quick modification of Gamefix options.
182 // Used by loadGameSettings() to set gamefixes via database at game startup.
183 Pcsx2Config::GamefixOptions& SetGameFixConfig()
184 {
185 //DbgCon.WriteLn( "Direct modification of EmuConfig.Gamefixes detected" );
186 AffinityAssert_AllowFrom_MainUI();
187 return const_cast<Pcsx2Config::GamefixOptions&>(EmuConfig.Gamefixes);
188 }
189
190 TraceLogFilters& SetTraceConfig()
191 {
192 //DbgCon.WriteLn( "Direct modification of EmuConfig.TraceLog detected" );
193 AffinityAssert_AllowFrom_MainUI();
194 return const_cast<TraceLogFilters&>(EmuConfig.Trace);
195 }
196
197
198 // This function should be called once during program execution.
199 void SysLogMachineCaps()
200 {
201 Console.WriteLn( Color_StrongGreen, "PCSX2 %u.%u.%u.r%d %s - compiled on " __DATE__,
202 PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo,
203 SVN_REV, SVN_MODS ? "(modded)" : ""
204 );
205
206 Console.WriteLn( "Savestate version: 0x%x", g_SaveVersion);
207 Console.Newline();
208
209 Console.WriteLn( Color_StrongBlack, "Host Machine Init:" );
210
211 Console.Indent().WriteLn(
212 L"Operating System = %s\n"
213 L"Physical RAM = %u MB",
214
215 GetOSVersionString().c_str(),
216 (u32)(GetPhysicalMemory() / _1mb)
217 );
218
219 u32 speed = x86caps.CalculateMHz();
220
221 Console.Indent().WriteLn(
222 L"CPU name = %s\n"
223 L"Vendor/Model = %s (stepping %02X)\n"
224 L"CPU speed = %u.%03u ghz (%u logical thread%s)\n"
225 L"x86PType = %s\n"
226 L"x86Flags = %08x %08x\n"
227 L"x86EFlags = %08x",
228 fromUTF8( x86caps.FamilyName ).Trim().Trim(false).c_str(),
229 fromUTF8( x86caps.VendorName ).c_str(), x86caps.StepID,
230 speed / 1000, speed % 1000,
231 x86caps.LogicalCores, (x86caps.LogicalCores==1) ? L"" : L"s",
232 x86caps.GetTypeName().c_str(),
233 x86caps.Flags, x86caps.Flags2,
234 x86caps.EFlags
235 );
236
237 Console.Newline();
238
239 wxArrayString features[2]; // 2 lines, for readability!
240
241 if( x86caps.hasMultimediaExtensions ) features[0].Add( L"MMX" );
242 if( x86caps.hasStreamingSIMDExtensions ) features[0].Add( L"SSE" );
243 if( x86caps.hasStreamingSIMD2Extensions ) features[0].Add( L"SSE2" );
244 if( x86caps.hasStreamingSIMD3Extensions ) features[0].Add( L"SSE3" );
245 if( x86caps.hasSupplementalStreamingSIMD3Extensions ) features[0].Add( L"SSSE3" );
246 if( x86caps.hasStreamingSIMD4Extensions ) features[0].Add( L"SSE4.1" );
247 if( x86caps.hasStreamingSIMD4Extensions2 ) features[0].Add( L"SSE4.2" );
248
249 if( x86caps.hasMultimediaExtensionsExt ) features[1].Add( L"MMX2 " );
250 if( x86caps.has3DNOWInstructionExtensions ) features[1].Add( L"3DNOW " );
251 if( x86caps.has3DNOWInstructionExtensionsExt ) features[1].Add( L"3DNOW2" );
252 if( x86caps.hasStreamingSIMD4ExtensionsA ) features[1].Add( L"SSE4a " );
253
254 const wxString result[2] =
255 {
256 JoinString( features[0], L".. " ),
257 JoinString( features[1], L".. " )
258 };
259
260 Console.WriteLn( Color_StrongBlack, L"x86 Features Detected:" );
261 Console.Indent().WriteLn( result[0] + (result[1].IsEmpty() ? L"" : (L"\n" + result[1])) );
262 Console.Newline();
263 }
264
265 template< typename CpuType >
266 class CpuInitializer
267 {
268 public:
269 ScopedPtr<CpuType> MyCpu;
270 ScopedExcept ExThrown;
271
272 CpuInitializer();
273 virtual ~CpuInitializer() throw();
274
275 bool IsAvailable() const
276 {
277 return !!MyCpu;
278 }
279
280 CpuType* GetPtr() { return MyCpu.GetPtr(); }
281 const CpuType* GetPtr() const { return MyCpu.GetPtr(); }
282
283 operator CpuType*() { return GetPtr(); }
284 operator const CpuType*() const { return GetPtr(); }
285 };
286
287 // --------------------------------------------------------------------------------------
288 // CpuInitializer Template
289 // --------------------------------------------------------------------------------------
290 // Helper for initializing various PCSX2 CPU providers, and handing errors and cleanup.
291 //
292 template< typename CpuType >
293 CpuInitializer< CpuType >::CpuInitializer()
294 {
295 try {
296 MyCpu = new CpuType();
297 MyCpu->Reserve();
298 }
299 catch( Exception::RuntimeError& ex )
300 {
301 Console.Error( L"CPU provider error:\n\t" + ex.FormatDiagnosticMessage() );
302 MyCpu = NULL;
303 ExThrown = ex.Clone();
304 }
305 catch( std::runtime_error& ex )
306 {
307 Console.Error( L"CPU provider error (STL Exception)\n\tDetails:" + fromUTF8( ex.what() ) );
308 MyCpu = NULL;
309 ExThrown = new Exception::RuntimeError(ex);
310 }
311 }
312
313 template< typename CpuType >
314 CpuInitializer< CpuType >::~CpuInitializer() throw()
315 {
316 if (MyCpu)
317 MyCpu->Shutdown();
318 }
319
320 // --------------------------------------------------------------------------------------
321 // CpuInitializerSet
322 // --------------------------------------------------------------------------------------
323 class CpuInitializerSet
324 {
325 public:
326 // Note: Allocate sVU first -- it's the most picky.
327
328 CpuInitializer<recSuperVU0> superVU0;
329 CpuInitializer<recSuperVU1> superVU1;
330
331 CpuInitializer<recMicroVU0> microVU0;
332 CpuInitializer<recMicroVU1> microVU1;
333
334 CpuInitializer<InterpVU0> interpVU0;
335 CpuInitializer<InterpVU1> interpVU1;
336
337 public:
338 CpuInitializerSet() {}
339 virtual ~CpuInitializerSet() throw() {}
340 };
341
342
343 // returns the translated error message for the Virtual Machine failing to allocate!
344 static wxString GetMemoryErrorVM()
345 {
346 return pxE( "!Notice:EmuCore::MemoryForVM",
347 L"PCSX2 is unable to allocate memory needed for the PS2 virtual machine. "
348 L"Close out some memory hogging background tasks and try again."
349 );
350 }
351
352 // --------------------------------------------------------------------------------------
353 // SysReserveVM (implementations)
354 // --------------------------------------------------------------------------------------
355 SysMainMemory::SysMainMemory()
356 {
357 }
358
359 SysMainMemory::~SysMainMemory() throw()
360 {
361 ReleaseAll();
362 }
363
364 void SysMainMemory::ReserveAll()
365 {
366 pxInstallSignalHandler();
367
368 DevCon.WriteLn( Color_StrongBlue, "Mapping host memory for virtual systems..." );
369 ConsoleIndentScope indent(1);
370
371 m_ee.Reserve();
372 m_iop.Reserve();
373 m_vu.Reserve();
374
375 reserveNewVif(0);
376 reserveNewVif(1);
377 }
378
379 void SysMainMemory::CommitAll()
380 {
381 vtlb_Core_Alloc();
382 if (m_ee.IsCommitted() && m_iop.IsCommitted() && m_vu.IsCommitted()) return;
383
384 DevCon.WriteLn( Color_StrongBlue, "Allocating host memory for virtual systems..." );
385 ConsoleIndentScope indent(1);
386
387 m_ee.Commit();
388 m_iop.Commit();
389 m_vu.Commit();
390 }
391
392
393 void SysMainMemory::ResetAll()
394 {
395 CommitAll();
396
397 DevCon.WriteLn( Color_StrongBlue, "Resetting host memory for virtual systems..." );
398 ConsoleIndentScope indent(1);
399
400 m_ee.Reset();
401 m_iop.Reset();
402 m_vu.Reset();
403
404 // Note: newVif is reset as part of other VIF structures.
405 }
406
407 void SysMainMemory::DecommitAll()
408 {
409 if (!m_ee.IsCommitted() && !m_iop.IsCommitted() && !m_vu.IsCommitted()) return;
410
411 Console.WriteLn( Color_Blue, "Decommitting host memory for virtual systems..." );
412 ConsoleIndentScope indent(1);
413
414 m_ee.Decommit();
415 m_iop.Decommit();
416 m_vu.Decommit();
417
418 closeNewVif(0);
419 closeNewVif(1);
420
421 vtlb_Core_Free();
422 }
423
424 void SysMainMemory::ReleaseAll()
425 {
426 DecommitAll();
427
428 Console.WriteLn( Color_Blue, "Releasing host memory maps for virtual systems..." );
429 ConsoleIndentScope indent(1);
430
431 vtlb_Core_Free(); // Just to be sure... (calling order could result in it getting missed during Decommit).
432
433 releaseNewVif(0);
434 releaseNewVif(1);
435
436 m_ee.Decommit();
437 m_iop.Decommit();
438 m_vu.Decommit();
439
440 safe_delete(Source_PageFault);
441 }
442
443
444 // --------------------------------------------------------------------------------------
445 // SysCpuProviderPack (implementations)
446 // --------------------------------------------------------------------------------------
447 SysCpuProviderPack::SysCpuProviderPack()
448 {
449 Console.WriteLn( Color_StrongBlue, "Reserving memory for recompilers..." );
450 ConsoleIndentScope indent(1);
451
452 CpuProviders = new CpuInitializerSet();
453
454 try {
455 recCpu.Reserve();
456 }
457 catch( Exception::RuntimeError& ex )
458 {
459 m_RecExceptionEE = ex.Clone();
460 Console.Error( L"EE Recompiler Reservation Failed:\n" + ex.FormatDiagnosticMessage() );
461 recCpu.Shutdown();
462 }
463
464 try {
465 psxRec.Reserve();
466 }
467 catch( Exception::RuntimeError& ex )
468 {
469 m_RecExceptionIOP = ex.Clone();
470 Console.Error( L"IOP Recompiler Reservation Failed:\n" + ex.FormatDiagnosticMessage() );
471 psxRec.Shutdown();
472 }
473
474 // hmm! : VU0 and VU1 pre-allocations should do sVU and mVU separately? Sounds complicated. :(
475 }
476
477 bool SysCpuProviderPack::IsRecAvailable_MicroVU0() const { return CpuProviders->microVU0.IsAvailable(); }
478 bool SysCpuProviderPack::IsRecAvailable_MicroVU1() const { return CpuProviders->microVU1.IsAvailable(); }
479 BaseException* SysCpuProviderPack::GetException_MicroVU0() const { return CpuProviders->microVU0.ExThrown; }
480 BaseException* SysCpuProviderPack::GetException_MicroVU1() const { return CpuProviders->microVU1.ExThrown; }
481
482 bool SysCpuProviderPack::IsRecAvailable_SuperVU0() const { return CpuProviders->superVU0.IsAvailable(); }
483 bool SysCpuProviderPack::IsRecAvailable_SuperVU1() const { return CpuProviders->superVU1.IsAvailable(); }
484 BaseException* SysCpuProviderPack::GetException_SuperVU0() const { return CpuProviders->superVU0.ExThrown; }
485 BaseException* SysCpuProviderPack::GetException_SuperVU1() const { return CpuProviders->superVU1.ExThrown; }
486
487
488 void SysCpuProviderPack::CleanupMess() throw()
489 {
490 try
491 {
492 psxRec.Shutdown();
493 recCpu.Shutdown();
494 }
495 DESTRUCTOR_CATCHALL
496 }
497
498 SysCpuProviderPack::~SysCpuProviderPack() throw()
499 {
500 CleanupMess();
501 }
502
503 bool SysCpuProviderPack::HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) const
504 {
505 return (recOpts.EnableEE && !IsRecAvailable_EE()) ||
506 (recOpts.EnableIOP && !IsRecAvailable_IOP()) ||
507 (recOpts.EnableVU0 && recOpts.UseMicroVU0 && !IsRecAvailable_MicroVU0()) ||
508 (recOpts.EnableVU1 && recOpts.UseMicroVU0 && !IsRecAvailable_MicroVU1()) ||
509 (recOpts.EnableVU0 && !recOpts.UseMicroVU0 && !IsRecAvailable_SuperVU0()) ||
510 (recOpts.EnableVU1 && !recOpts.UseMicroVU1 && !IsRecAvailable_SuperVU1());
511
512 }
513
514 BaseVUmicroCPU* CpuVU0 = NULL;
515 BaseVUmicroCPU* CpuVU1 = NULL;
516
517 void SysCpuProviderPack::ApplyConfig() const
518 {
519 Cpu = CHECK_EEREC ? &recCpu : &intCpu;
520 psxCpu = CHECK_IOPREC ? &psxRec : &psxInt;
521
522 CpuVU0 = CpuProviders->interpVU0;
523 CpuVU1 = CpuProviders->interpVU1;
524
525 if( EmuConfig.Cpu.Recompiler.EnableVU0 )
526 CpuVU0 = EmuConfig.Cpu.Recompiler.UseMicroVU0 ? (BaseVUmicroCPU*)CpuProviders->microVU0 : (BaseVUmicroCPU*)CpuProviders->superVU0;
527
528 if( EmuConfig.Cpu.Recompiler.EnableVU1 )
529 CpuVU1 = EmuConfig.Cpu.Recompiler.UseMicroVU1 ? (BaseVUmicroCPU*)CpuProviders->microVU1 : (BaseVUmicroCPU*)CpuProviders->superVU1;
530 }
531
532 // This is a semi-hacky function for convenience
533 BaseVUmicroCPU* SysCpuProviderPack::getVUprovider(int whichProvider, int vuIndex) const {
534 switch (whichProvider) {
535 case 0: return vuIndex ? (BaseVUmicroCPU*)CpuProviders->interpVU1 : (BaseVUmicroCPU*)CpuProviders->interpVU0;
536 case 1: return vuIndex ? (BaseVUmicroCPU*)CpuProviders->superVU1 : (BaseVUmicroCPU*)CpuProviders->superVU0;
537 case 2: return vuIndex ? (BaseVUmicroCPU*)CpuProviders->microVU1 : (BaseVUmicroCPU*)CpuProviders->microVU0;
538 }
539 return NULL;
540 }
541
542 // Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition.
543 // This can be called at any time outside the context of a Cpu->Execute() block without
544 // bad things happening (recompilers will slow down for a brief moment since rec code blocks
545 // are dumped).
546 // Use this method to reset the recs when important global pointers like the MTGS are re-assigned.
547 void SysClearExecutionCache()
548 {
549 GetCpuProviders().ApplyConfig();
550
551 Cpu->Reset();
552 psxCpu->Reset();
553
554 // mVU's VU0 needs to be properly initialized for macro mode even if it's not used for micro mode!
555 if (CHECK_EEREC)
556 ((BaseVUmicroCPU*)GetCpuProviders().CpuProviders->microVU0)->Reset();
557
558 CpuVU0->Reset();
559 CpuVU1->Reset();
560
561 resetNewVif(0);
562 resetNewVif(1);
563 }
564
565 // Maps a block of memory for use as a recompiled code buffer, and ensures that the
566 // allocation is below a certain memory address (specified in "bounds" parameter).
567 // The allocated block has code execution privileges.
568 // Returns NULL on allocation failure.
569 u8* SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller)
570 {
571 u8* Mem = (u8*)HostSys::Mmap( base, size );
572
573 if( (Mem == NULL) || (bounds != 0 && (((uptr)Mem + size) > bounds)) )
574 {
575 if( base )
576 {
577 DbgCon.Warning( "First try failed allocating %s at address 0x%x", caller, base );
578
579 // Let's try again at an OS-picked memory area, and then hope it meets needed
580 // boundschecking criteria below.
581 SafeSysMunmap( Mem, size );
582 Mem = (u8*)HostSys::Mmap( 0, size );
583 }
584
585 if( (bounds != 0) && (((uptr)Mem + size) > bounds) )
586 {
587 DevCon.Warning( "Second try failed allocating %s, block ptr 0x%x does not meet required criteria.", caller, Mem );
588 SafeSysMunmap( Mem, size );
589
590 // returns NULL, caller should throw an exception.
591 }
592 }
593 return Mem;
594 }
595
596 // This function always returns a valid DiscID -- using the Sony serial when possible, and
597 // falling back on the CRC checksum of the ELF binary if the PS2 software being run is
598 // homebrew or some other serial-less item.
599 wxString SysGetDiscID()
600 {
601 if( !DiscSerial.IsEmpty() ) return DiscSerial;
602
603 if( !ElfCRC )
604 {
605 // FIXME: If the system is currently running the BIOS, it should return a serial based on
606 // the BIOS being run (either a checksum of the BIOS roms, and/or a string based on BIOS
607 // region and revision).
608 }
609
610 return pxsFmt( L"%08x", ElfCRC );
611 }

  ViewVC Help
Powered by ViewVC 1.1.22